home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
t3_1
/
manual.txt
< prev
next >
Wrap
Text File
|
1993-07-09
|
344KB
|
8,290 lines
THE T MANUAL
Fourth Edition
10 September 1988
Jonathan A. Rees
Norman I. Adams
James R. Meehan
Yale University
Copyright 1988 Computer Science Department, Yale University,
New Haven CT
modified version,
alk, 12/23/88
Preface
This manual describes T, a programming language being developed at the Yale
University Computer Science Department [Rees82], and its current
implementations. It is a reference manual, intended to define the language and
to describe the general direction of its development. It is not intended as an
introduction or a tutorial.
The bulk of this document describes T as a programming language. However,
chapters 17, 18, and 19 describe pragmatic features which are specific to the
current implementations. To help underscore the distinction between language
and implementation, the implementations are referred to not as T but as Tau.
The reader is expected to have some programming experience; the reader may also
find a knowledge of Lisp helpful, although T is different enough from Lisp that
prior experience with Lisp might actually be a hindrance.
Chapter F describes ways in which the language is deficient and directions in
which it is likely to grow. Also, the implementations are not faithful to the
language definition presented in the manual; section F.3 catalogs known
deviations.
Please send questions and comments about the language, this manual, or the
various implementations of the system, via ARPAnet mail to Rees@Yale.ARPA,
Usenet mail to ...decvax!yale-comix!rees, or via U.S. Mail to
Jonathan Rees
Department of Computer Science
Yale University
P.O. Box 2158 Yale Station
New Haven, Connecticut 06520
Telephone (203) 432-4666
Announcements of interest to T users are regularly broadcast via Arpanet and
Usenet electronic mail to the mailing list T-Users@Yale.ARPA; send mail to
T-Users-Request@Yale.ARPA if you want to be added to (or removed from) this
list. Send reports about errors in the manual or implementations to
T-Bugs@Yale.ARPA.
James R. Meehan's current address is:
Cognitive Systems, Inc.
234 Church St.
New Haven, Connecticut 06510
The T project has been funded exclusively by the Computing Facility of the Yale
Computer Science Department.
TOPS-20, VMS, and VAX are trademarks of Digital Equipment Corporation. Domain
and Aegis are trademarks of Apollo Computer, Inc. UNIX is a trademark of Bell
Laboratories.
-i-
Notes on this edition
The fourth edition includes, among others, the following additions and changes,
many of which correspond to new features in Tau version 2.7:
- New procedures: bitwise logical operators, MAKE-LOCALE,
MAKE-EMPTY-LOCALE, LOCALE? *VALUE, *DEFINE, *LSET, CHAR->STRING,
ASSERT, SYNTAX-ERROR, READ-ERROR, PROJ1 et al., OBJECT-HASH,
OBJECT-UNHASH, WALK-WEAK-SET, MAKE-LIST-READER, MAKE-READ-TABLE,
MACRO-EXPANDER?, INVOKE-MACRO-EXPANDER, OBJECT-FILE?,
OBJECT-FILE-STREAM?, TC-SYNTAX-TABLE,
WRITE-LINE, WRITE-SPACES, MAKE-BROADCAST-PORT.
- New special forms: IMPORT, MODIFY, DEFINE-LOCAL-SYNTAX,
MACRO-EXPANDER, COMMENT, IGNORE, IGNORABLE.
- New character names (LEFT-PAREN and others).
- Escape character syntax explained.
- Sample implementation of DELAY and FORCE provided.
- Name changes: *THE-USER-ENV* is now called USER-ENV;
*THE-BASE-ENV* is STANDARD-ENV; DEFINE-MACRO is DEFINE-SYNTAX;
GEN-ID is GENERATE-SYMBOL; SYMBOLCONC is CONCATENATE-SYMBOL.
- Features withdrawn: FLOOR, CEILING, ENV-LOOKUP, CONS-FROM-FREELIST,
RETURN-TO-FREELIST, SET-DELIMITED-LIST-SYNTAX-IN-TABLE, dispatch read
macros. [TC-MACRO-DEFINITION-ENV.]
-ii-
This document is still far from finished. Future work required includes the
following projects.
- The preface ought to have a paragraph or two describing the
organization of the manual.
- The "language overview" and "language principles" sections need to be
expanded. There should be a description of the memory model of
objects, in which objects are modeled by pointers into memory.
- The form of procedure and special form description should be
explained.
- The section on types needs expansion and examples.
- Tail recursion should be explained.
- MAKE-ECHO-STREAM should be described.
- The external syntax of numbers should be described. Chapter 14 needs
to give more detail.
- Need to document HANDLER so that people know what to set a structure
handler to. Need to describe keyword clauses in OBJECT syntax.
- There should be examples of the use of JOIN in implementing type
hierarchies and heterarchies.
- The introduction to the ports chapter needs work. Also, port
positions should be documented and implemented.
- There should be a section giving a brief description of how the
implementation works at the machine level. A full description should
be relegated to an independent document, which should be written.
- Need to discuss the VM system and reorganize the presentation of the
user interface. Many more remarks on error handling and debugging
are in order.
-iii-
Acknowledgements
The authors wish to thank Kent Pitman for his continuing assistance in making T
true.
Gerry Sussman has provided essential guidance and inspiration.
Guy Steele wrote most of the compiler we're using, and has otherwise been a
strong influence, originating much of the project's design philosophy and
shaping and inspiring many of its features.
This document borrows from the Lisp Machine Manual [WEINREB81] and the Common
Lisp Reference Manual [STEELE81COM]. We are grateful to their authors.
We wish to acknowledge the influence and valuable advice provided during the
design process by the following people at Yale, MIT, and elsewhere: Alan
Bawden, Richard Bryan, David Byrne, George Carrette, William Clinger, Peter
Deutsch, John Ellis, William Ferguson, Christopher Hanson, Carl Hoffman, David
Kranz, David Littleboy, Drew McDermott, Nathaniel Mishkin, Robert Nix, Jim
Philbin, John Ramsdell, Christopher Riesbeck, John Ruttenberg, Olin Shivers,
and Steve Wood.
Thanks to Judy Martel for her patient proofreading.
The NIL project at MIT was the source of many of T's good ideas. NIL is the
work of Richard Bryan, Glenn Burke, George Carrette, Michael Genereseth, Robert
Kerns, Jim Purtilo, John White, and one of the present authors (JR).
Jim Purtilo wrote the integer arithmetic package used in the current
implementation while he was working on the NIL project.
We appreciate the patience of our user community, at Yale and elsewhere, who
have had to put up with an incompatible, incomplete, and untuned new system.
Finally, we wish to thank John O'Donnell for helping to shelter us from the
real world, and for having the foresight, or perhaps folly, to initiate the
project in the first place.
-iv-
Table of Contents
1. Introduction 1
1.1. Language overview 1
1.2. Notational conventions 1
1.3. Naming conventions 2
1.4. Language principles and conventions 3
2. Syntax and semantics 4
2.1. External representation 4
2.2. Core language 5
2.3. The standard environment 6
2.4. Undefined 6
3. Objects 7
3.1. Literals 7
3.2. Procedures 8
3.3. Object identity 8
3.4. Symbols 8
3.5. Predicates and truth values 9
3.6. Types 10
4. Environments 11
4.1. Environments and contours 11
4.2. Local variables 12
4.3. Locales 14
4.4. Non-local reference 16
5. Control 18
5.1. Conditionals 18
5.2. Iteration 21
5.3. Procedure application 22
5.4. Sequencing 23
5.5. Non-local exits 23
5.6. Lazy evaluation 24
6. Side effects 25
6.1. Assignment 25
6.2. Locatives 27
6.3. Dynamic state 28
7. Operations 30
7.1. Fundamental forms 30
7.2. Defining operations 33
7.3. Joined objects 34
7.4. Synonyms 34
7.5. Example 35
-v-
8. Numbers 36
8.1. Predicates 36
8.2. Arithmetic 37
8.3. Comparison 38
8.4. Sign predicates 39
8.5. Transcendental functions 40
8.6. Bitwise logical operators 41
8.7. Coercion 42
8.8. Assignment 42
9. Lists 43
9.1. Predicates 43
9.2. Constructors 44
9.3. List access 44
9.4. Lists as sequences 46
9.5. Lists as sets 47
9.6. Mapping Procedures 48
9.7. Lists as associations 49
9.8. Lists as stacks 49
10. Trees 50
10.1. Comparison 51
10.2. Tree utilities 51
10.3. Destructuring 52
10.4. Quasiquote 53
11. Structures 55 X11
11.1. Terminology 55
11.2. Defining structure types 55
11.3. Manipulating structure types 56
11.4. Manipulating structures 58
12. Characters and strings 60
12.1. Predicates 61
12.2. Comparison 62
12.3. String constructors 63
12.4. String access 63
12.5. String manipulation 65
12.6. String header manipulation 65
12.7. Case conversion 66
12.8. Digit conversion 66
12.9. ASCII conversion 67
12.10. Symbols 68
13. Miscellaneous features 69
13.1. Comments and declarations 69
13.2. Errors and dead ends 69
13.3. Early binding 71
13.4. Symbol generators 71
13.5. Combinators 71
13.6. Vectors 73
13.7. Pools 75
13.8. Weak pointers 75
-vi-
14. Syntax 77
14.1. The reader 77 X10
14.2. Read tables and read macros 79 X10
14.3. Standard compiler 81
14.4. Syntax tables 82 X10
14.5. Defining syntax 83
14.6. Local syntax 84
14.7. Macro expanders 85
15. Ports 87
15.1. General 87
15.2. Port switches 88
15.3. Input 89
15.4. Output 91
15.5. Formatted output 92
15.6. Miscellaneous 93
15.7. Example 94
16. Files 95
16.1. File systems 95
16.2. Filenames 96
16.3. Files 97
17. Program structure 100
17.1. Environment structure 100
17.2. Source files 101
17.3. File syntax 101
17.4. Loading files 102
17.5. File compilation 103
18. User interface 105
18.1. Invoking Tau 105
18.2. Read-eval-print loops 106
18.3. Command levels 107
18.4. Transcripts 107
18.5. Customization 108
19. Debugging 109
19.1. Errors 109
19.2. Debugging utilities 110
19.3. The inspector 111
19.4. Debugging primitives 114
19.5. Miscellaneous 115
-vii-
Appendix A: A meta-circular evaluator . . . . . . . . . . . 116
Appendix B: Libraries. . . . . . . . . . . . . . . . . 120
B.1. The data base 120
B.2. Symbol tables 121
B.3. List utilities 122
B.4. Type-specific arithmetic 123
Appendix C: ASCII character conversion . . . . . . . . . . . 125
Appendix D: Equivalences. . . . . . . . . . . . . . . . 126
Appendix E: Friendly advice. . . . . . . . . . . . . . . 129
E.1. Comparison with other Lisp dialects 129
E.2. Incompatibilities 129
Appendix F: Future work . . . . . . . . . . . . . . . . 130
F.1. Language design problems 130
F.2. Common Lisp influence 132
F.3. Bugs in the implementation 133
References 135
Index 136
________________________________________________________________________________
Notice to the Reader
Marginal cross-references to the T 3.0 Release Notes and to the T 3.1 release
notes have been added to this variant of the fourth edition T Reference Manual.
They consist of marginal entries of the form Xn, where n is either a page number
in the T 3.0 Release Notes, or an asterisk, *, signifying a reference to the
T 3.1 release notes. These cross-references do not indicate additions to the
language, such as the load-foreign interface, or tables (replacing property
lists), or the new call-with-current-continuation and new i/o procedures. For
more information on these, consult the release notes. --alk
-vii-
Chapter 1 Introduction
1.1. Language overview
T is a dialect of Lisp derived from SCHEME. [STEELE78REV] It is comparable in
power and expressiveness to other recent Lisp dialects such as Lisp Machine
Lisp, [WEINREB81] Common Lisp, [STEELE83COM] and NIL, [WHITE79] but
fundamentally more similar in spirit to SCHEME than these other Lisp dialects.
T endeavors to be a powerful, expressive language which also permits portable
and efficient implementations. T is a "modern" Lisp suitable for a broad
variety of applications, including symbolic, numerical, and systems
programming, on a variety of machines.
T draws from many sources in its attempt to provide a complete yet simple and
unified system. For the most part, the language's designers are collators, not
originators. Common Lisp, NIL, and SCHEME have been the strongest influences.
Except for a restriction on the use of escape procedures (see the description
of CATCH,page 23), the omission of TEST and the multiprocessing primitives, and
a few changed names (e.g., SET instead of ASET'), T includes as a subset the
SCHEME dialect described in The Revised Report on SCHEME [STEELE78REV].
1.2. Notational conventions
The symbol "=>" is used to indicate evaluation. (Evaluation is described in
section 2.2.) For example, (+ 3 5) => 8 means that evaluating the expression
(+ 3 5) yields 8.
The symbol "<=>" indicates code equivalence. For example,
(CAR (CDR x)) <=> (CADR x)
means that for any x, the value and effects of (CAR (CDR x)) are always the
same as the value and effects of (CADR x).
The symbol "->" is used in definitions to indicate the type of result. For
example,
(NUMBER? object) -> boolean
means that the procedure named NUMBER? yields a boolean result.
(PRINT object port) -> undefined
means that PRINT returns a value of no particular interest; that is, one should
not depend on it to return anything in particular.
Italicized names in the code examples are meta-variables that stand for pieces
of code to be filled in. Restrictions on the particular types or values of a
meta-variable are often suggested by its name or are given in the text
associated with the example. The term object denotes data of unrestricted
type.
Dot-notation, as in (ADD . numbers), is used where any number of sub-forms are
permitted. In this example, there may be zero or more numbers; the description
subsumes cases such as (ADD), (ADD 3), and (ADD 3 5 2).
-1-
Some routines described in this manual, such as VREF (page 74), serve as access
routines in the sense that they are appropriate for use in SET and related
forms to designate locations. These routines have their descriptions flagged
with the notation Settable.
1.3. Naming conventions
Below are listed some of the naming conventions used for standard T procedures,
variables, and reserved words.
...? The suffix ? indicates a predicate. For example, ZERO? is a
predicate which returns true if its argument is numerically
equal to zero.
...! The suffix ! indicates a side-effecting variant of a
non-side-effecting procedure. For example, REVERSE! is the
same as REVERSE, but it alters its argument, recycling the
storage to form its result.
...->... Infix -> is used in names of coercion procedures, which are
routines which perform conversions between objects of different
types. For example, given a string, STRING->LIST forms a list
of the characters in the string.
*...* The names of variables whose scope is not visually apparent to
someone reading the program is usually bounded on either side
with *'s. This convention is used for variables which are
either not bound to procedures, or whose value is intended to
change.
DEFINE-... Relates to the assignment of something in a permanent way.
DEFINE is typically used for defining procedures,
DEFINE-OPERATION to define generic operations, and so forth.
MAKE-... Indicates a routine which creates an object of some type, given
size (and sometimes datatype) information. For example,
(MAKE-STRING 5) makes a string of length 5.
NOT-... The prefix "NOT-" means that the truth value of some predicate
is complemented. For example, (NOT-LESS? x y) is the same as
(NOT (LESS? x y)).
...CDR Relates to the subtail or successive subtails of a list. For
th
example, while the procedure named NTH returns the n element
of a list, there is another procedure named NTHCDR that returns
th
the n subtail.
...Q The presence of the trailing character "Q" on a name typically
means that the predicate "EQ?" is involved. For example,
(MEMQ? x l) <=> (MEM? EQ? x l)
-2-
1.4. Language principles and conventions
The design of T employs a number of conventions. These conventions make the
language more regular, predictable, and easy to learn.
Anonymity: objects are not tightly coupled to their names.
Locality: effects may be achieved locally.
Argument order: procedures which extract components from aggregate objects
generally take the aggregate object as the first argument, and selection
information (if any) as subsequent arguments. Similarly, assignment procedures
take the aggregate object or location specification early, and the value to be
stored last.
Indexing: where numeric indices are involved, the indices begin with zero and
range up to a given limit. Ranges are specified as half-open intervals,
inclusive at the low end and exclusive at the high end.
-3-
Chapter 2 Syntax and semantics
This chapter gives an overview of T as a programming language. It is important
not to confuse the language per se with a particular body of software which
implements it. It is natural that this confusion arises, both because the
language and its implementations have developed in parallel, and because this
confusion is traditional among Lisp dialects (for example, Maclisp and Lisp
Machine Lisp).
The T language logically comprises three distinct components:
- an external representation for objects as character sequences,
- a core language -- syntax and semantics for expressions, and
- a standard environment -- the behavior of the objects which are
values of system variables.
2.1. External representation
Like other Lisp dialects, and in contrast with most other programming
languages, T has two different kinds of syntax: the external representation by
which objects (data) are represented as a linear sequence of characters, and an
expression syntax by which these objects can be understood as programs. That
is, the meaning of a T program represented as characters in a file or other
external storage medium must be determined in two stages: first, by mapping
the characters to objects, and then by interpreting these objects as executable
programs.
The external syntax of T is very similar to that of other Lisp dialects, and is
discussed in detail at appropriate places in this manual. The following gives
only the most cursory description.
Characters may be classified according to their lexical properties; the two
most important distinctions are between constituent and delimiter characters,
and between read-macro characters and non-read-macro characters. Alphabetic,
numeric, and some special characters (e.g. - and $) are constituent characters;
whitespace and some special characters (e.g. left and right parenthesis) are
delimiter characters. Some special characters such as ( and ' are read-macro
characters; these introduce special syntactic constructs which are
idiosyncratic to the particular character introducing them.
A delimited sequence of consecutive constituent characters represents either a
number or a symbol. For example, 255 represents the integer 255, and APPEND
and append both represent the symbol APPEND. Here, and throughout the manual,
the term symbol is being used in a technical manner to refer a particular kind
of named object (see section 3.4).
Balanced parentheses with a sequence of (representations of) objects between
them represent a list of the (represented) objects. For example, the
characters (A B (C 12) D) represent a list of four objects, three of which are
symbols, and one of which (the third one) is a list of two objects, the symbol
C and the integer 12. Parentheses which enclose no objects represent an empty
list.
There are also external representations for strings and characters, as well as
for some kinds of objects which are syntactically illegal as expressions (for
example, vectors). Not all objects have external representations, however.
-4-
2.2. Core language
The core language is described in terms of a hypothetical machine (called an
evaluator) which executes T code directly. In practice, the existing T
implementations have (at least) two evaluators, both of which perform
evaluation as a two-stage process consisting of compilation (syntactic and
semantic analysis) followed by interpretation.
Evaluation is a process whereby an object called an expression (the term form
is used synonymously with expression) is mapped to another object, called its
value. Evaluation occurs in the context of a particular variable environment;
see below. The expression is said to yield its value.
The evaluation mapping is not a purely mathematical mapping, since in some
cases the evaluation of an expression may depend not only on the variable
environment but on the state of the running T system, or on the state of the
world outside it; and evaluation may cause changes in the state of system or
the world, which may, in turn, affect future evaluations. These state changes
are called side-effects.
An evaluator for the core language, written in T, can be found in Appendix A.
This program simply encodes in a formal way the evaluation process (that is,
the semantics) described informally below.
The rules by which an object is evaluated are as follows:
Self-evaluating literals: All numbers, strings, and characters are
syntactically valid expressions which, when evaluated, yield themselves. For
example,
-2102 => -2102
#\M => #\M
"A string." => "A string."
The notation "expression => value" means that expression, when evaluated,
yields value. (Note also that we are making use of the external object syntax
itself as a notational device: "the object -2102" could be said more precisely
as "an object externally represented by the characters `-2102'." Just as a
program should never be confused with an object which represents it, an object
should never be confused with a sequence of characters which notates it.)
Symbols: As evaluable expressions, symbols are interpreted as variable
references. A symbol evaluates to its value according to the current variable
environment. For example, in an environment in which the variable DELTA has
the value 15, evaluating the expression DELTA will yield 15.
Symbols have many uses other than as names for variables. It is important not
to confuse the use of a symbol as a datum manipulated by a program with the
occurrence of a symbol as a variable reference in a program. Symbols do not
have values a priori; variables only have values by virtue of the context in
which they occur.
Lists: Non-empty lists are classified either as calls or as special forms. If
the first element of the list is a symbol, and the symbol is a reserved word,
then the list is a special form; otherwise it is considered to be a call. T
reserved words include, for example, the symbols QUOTE, IF, and LAMBDA.
(However, see section 14.4.)
-5-
Special forms: The syntax and semantics of a special form are idiosyncratic to
the reserved word which introduces it; descriptions of the meaning of
expressions introduced by the various reserved words are therefore distributed
throughout the manual.
Calls: Calls are evaluated as follows: the elements of the list (including
the first) are evaluated, in no particular order. The first must evaluate to a
procedure; this procedure is applied to the rest of the values, which are
called arguments. (The verbs call and invoke mean the same as apply.)
2.3. The standard environment
The standard environment corresponds to what is usually known as the "run-time
library" in other language environments such as C or Pascal.
New variable environments may be introduced in various ways (see chapter 4),
but a T system is obliged to supply one standard environment in which system
variables are bound to system procedures and constants, as defined by this
manual. Program execution typically occurs in an environment inferior to this
standard environment (see section 17.1), so that these objects are easily
accessible as values of lexically apparent variables. For example, in this
standard environment, the variable CONS has a certain procedure as its value.
For the most part, the values of system variables are procedures, and therefore
the only behavior of interest is what they do when called. (See the discussion
of calls, above.) In other cases, values are objects such as numbers or
symbols.
A representation of the standard environment is available as the value of
STANDARD-ENV (page 100).
2.4. Undefined
The term undefined is used in two different ways in this manual. An expression
may yield an undefined value, in which case it yields some value, the
particular value not being defined by this manual. In such cases it is unwise
to depend on this value having any particular characteristics, for example, it
being null, or not a number, or whatever. The evaluation of the expression and
the creation of the undefined value are not in error; it is the use to which
this value is put that may lead to problems. Expressions yielding undefined
values are generally useful only for any side-effects they cause.
On the other hand, the evaluation of an expression may have an undefined
effect, in which case an implementation will endeavor to signal an error
condition, permitting a user to take appropriate action. For example, a call
to a non-procedure, or adding two symbols together, have undefined effects. An
implementation is not obliged, however, to signal an error, and in fact it may
be in the interest of efficiency to avoid the overhead of detecting
circumstances under which undefined effects will happen.
The two procedures UNDEFINED-VALUE and UNDEFINED-EFFECT are used for expository
purposes in examples throughout this manual; they are described in section
13.2.
-6-
Chapter 3 Objects
T is an object-oriented language. T programs are concerned for the most part
with creating and manipulating objects, which represent all data, and form the
currency of computation in T. Particular objects are defined not by bit
patterns or by addresses within a computer but rather by their behavior when
called or when passed to procedures which manipulate them.
Objects are obtained most primitively through the use of QUOTE (page 7), LAMBDA
(page 8), and OBJECT (page 31) special forms, and less primitively by calling
procedures defined in the standard environment, e.g. CONS and COMPOSE, which
create new objects or return existing ones. (An implementation of T would
presumably define many such procedures using more primitive object
constructors; for example, COMPOSE might be defined by a T program which
employs LAMBDA to construct the composed procedure. Thus the question of what
kinds of objects are truly primitive and which are not is left unanswered by
this manual.)
3.1. Literals
As described in section 2.2, some expressions evaluate "to themselves" or to
copies of themselves. These include numbers, strings, and characters, and are
called self-evaluating literals. However, some expressions, lists and symbols
in particular, do not evaluate to themselves. When an expression yielding a
particular constant value is required, it is necessary to use QUOTE.
Self-evaluating literals and quoted constants are called literals.
Although not all objects have external representations, some objects which do
have external representations have undefined evaluation semantics. These
include vectors (section 13.6) and the empty list. QUOTE must also be used to
obtain these values as constants.
(QUOTE object) -> object Special form
Yields object. The object is not evaluated; thus QUOTE provides a literal
notation for constants.
(QUOTE A) => A
(QUOTE (A B)) => (A B)
(QUOTE (+ X 8)) => (+ X 8)
(QUOTE (QUOTE A)) => (QUOTE A)
Since QUOTE is used so frequently, an abbreviated external syntax is
provided for QUOTE forms: 'object is an alternative external
representation for the list (QUOTE object).
'A => A
'(A B) => (A B)
'(QUOTE A) => (QUOTE A)
''A => (QUOTE A)
Objects returned by literal expressions are read-only; they should not be
altered using SET or any other side-effecting form.
-7-
3.2. Procedures
A procedure (or routine) is any object which may be called. Ordinarily, a
procedure is called as a result of the evaluation of a call (page 3). The most
primitive mechanism for creating procedures is the LAMBDA special form.
(LAMBDA variables . body) -> procedure Special form
A LAMBDA-expression evaluates to a procedure. When the procedure is
called, the variables are bound to the arguments to the call, and the
body, an implicit block, is evaluated. The call yields the value of the
last form in the body.
((LAMBDA (X Y) (LIST Y X)) 7 'FOO) => (FOO 7)
If variables is not a proper list, but ends in a symbol x, then the
variable x will be bound to a list of the arguments beginning at the
position corresponding to x.
((LAMBDA (X . Y) (LIST Y X)) 7 'FOO 'BAZ) => ((FOO BAZ) 7)
((LAMBDA Y Y) 7 'FOO 'BAZ) => (7 FOO BAZ)
If any variable is () instead of a symbol, then the corresponding argument
in a call is ignored.
Scoping: The values of the bound variables are apparent only in code
lexically contained in the body of the LAMBDA-expression, and not to
routines called from the body. That is, like SCHEME and ALGOL and unlike
most Lisp dialects, T is a lexically scoped language, not a dynamically
scoped language.
Closure: The procedure is said to be a closure of the LAMBDA-expression
in the current lexical environment. That is, when procedure is called,
the body is evaluated in an environment which is the same as that in which
the LAMBDA-expression was evaluated, augmented by the bindings of the
variables. For example, if Z is mentioned in the body, and it is not one
of the variables, then it refers to whatever Z was in scope when the
LAMBDA-expression was evaluated, not (necessarily) to the the variable Z
that is in scope when procedure is called.
3.3. Object identity
Every object has identity, in the sense that one may determine whether two
given values are the same object.
In the following:
(DEFINE A (LIST 'P 'Q))
(DEFINE B (LIST 'P 'Q))
there is no way (other than EQ?) to distinguish the two objects which are the
values of A and B. Nonetheless, since LIST is defined to return a new object
each time it is called, the two objects are distinct; and indeed, a side-effect
to one object will not affect the value of the other:
(SET (CAR A) 'R)
A => (R Q)
B => (P Q)
-8-
Some system procedures and special forms create new objects, others return
objects which already exist. In some cases, it is not defined which of the two
happens; it is only guaranteed that some object with appropriate
characteristics is returned. For example, CONS creates new objects; QUOTE
expressions yield pre-existing constant objects; and numerical routines such as
+ may or may not create new numbers.
The EQ? predicate primitively determines object identity.
(EQ? object1 object2) -> boolean
Returns true if object1 and object2 are identically the same object. EQ?
is the finest comparison predicate for objects, in the sense that if EQ?
cannot distinguish two objects, then neither can any other equality
predicate.
(NEQ? object1 object2) -> boolean
NEQ? is the logical complement of EQ?.
(NEQ? object1 object2) <=> (NOT (EQ? object1 object2))
Uniqueness of literals (other than symbols and characters) isn't defined in
general. For example,
(EQ? '(A B C) '(A B C))
may yield either true or false, depending on the implementation. Two
similar-looking literal expressions in different places may or may not yield
distinct objects.
However, a given literal expression will always yield the same object each time
it is evaluated.
(LET ((F (LAMBDA () '(A B C)))) (EQ? (F) (F))) -> true
3.4. Symbols
Symbols are named objects which have wide applicability as tokens and
identifiers of various sorts. Symbols are uniquely determined by their names,
and have a convenient external syntax by which they may be obtained.
Uniqueness of symbols is defined:
(EQ? 'FOO 'FOO) => true
(SYMBOL? object) -> boolean Type predicate
Returns true if object is a symbol.
See also SYMBOL->STRING and STRING->SYMBOL, page 68.
-9-
3.5. Predicates and truth values
Conditional expressions in T (see section 5.1) usually involve the evaluation
of a test expression; the object yielded by the test is then used to make a
control decision, depending on whether the value is false or true. There is
one false value; this is a distinguished object called null. Any other value
is considered to be true. A value intended to be used in this way is called a
truth value.
A predicate is any procedure which yields truth values. Predicates are
typically given names which end in ?, e.g. NULL? and EQ?. Calls to predicates
are naturally used as test expressions.
An equality predicate is a two-argument predicate which is side-effectless and
unaffected by side-effects, and acts like an equivalence relation in the
mathematical sense.
NIL -> false X6,7
This system variable has as its value the object null. Null serves two
distinct purposes in T: it is the standard false value, as returned by
test expressions and tested by conditionals; and it is the empty list, a
list which has no elements. Null's external representation is ().
(Programmers with prior LISP experience should note that in most other
dialects of LISP, null and the symbol NIL are identical. This is not the
case in T: 'NIL => NIL, but NIL => (), and (EQ? NIL 'NIL) is false.)
T -> true X6
The system variable T has as its value a standard true value. The
identity of this value is unimportant, since any non-false value is
considered to be true for the purposes of tests.
3.6. Types
A type may be seen either as a collection of objects with similar behavior, or
as a characteristic function for such a collection, or as the behavior
characteristic of similar objects; these views are equivalent.
A type predicate is a predicate which is defined on all objects and whose value
is not affected by any side-effects (that is, calling the type predicate on a
particular object will return the same value regardless of the point at which
it is called with respect to arbitrary other computations). Type predicates
are usually used to determine a given object's membership in a type.
-10-
Chapter 4 Environments
4.1. Environments and contours
Environments are associations between identifiers (variable names) and values.
One such association between an identifier and a value is called a binding. In
general, environments are created implicitly, for example, on entering
LAMBDA-bodies.
Environments are organized hierarchically into contours. Each contour augments
an "outer" environment, providing bindings for a few identifiers. These
bindings are in effect in the body of the expression which introduces the
contour. The portion of a program in which a binding is in effect is known as
the variable's scope.
For example:
(BLOCK
(LIST 'A 'B)
...
(LAMBDA (A B C)
... ; [1]
(LAMBDA (D E)
... ; [2]
)
... ; [3]
(LAMBDA (A F G)
... ; [4]
)
...)
...)
Each LAMBDA introduces a new contour. This code presumably occurs in an
outermost environment in which the variable LIST is bound. (The values of
BLOCK and LAMBDA are not in question here; they are reserved words, and their
bindings as variables are irrelevant.) At point [1] in the program, a new
environment is in effect which now has bindings for A, B, and C, as well as for
any variables known in the outer environment. At [2], D and E also have
values. [3] is in the scope of A, B, and C but not in the scope of D and E.
Finally, the outer binding of any given identifier is shadowed by inner
bindings of that identifier; an occurrence of the identifier A appearing at [4]
refers not to the outer A but to the inner one, because the inner one shadows
the outer.
There are two kinds of contours, known as lambda-contours and locales.
Lambda-contours are introduced by many special forms, such as LAMBDA, LET, and
LABELS. Locales are introduced by LOCALE special forms.
-11-
4.2. Local variables
(LET specs . body) -> value-of-body Special form
LET provides a convenient syntax for introducing local bindings. Each
spec should be a two-element list of the form (variable value). The value
expressions are all evaluated (in no particular order), then the body (an
implicit block) is evaluated in an environment where the variables are
bound to those values. The value of body is yielded.
(LET ((X 2)) X) => 2
(LET ((var val ) (var val ) ... (var val )) . body)
1 1 2 2 n n
<=> ((LAMBDA (var var ... var ) . body) val val ... val )
1 2 n 1 2 n
See also DESTRUCTURE (page 52).
(LET* specs . body) -> value-of-body Special form
LET* is like LET, except that the binding of variables to values is done
sequentially, so that the second binding is done in an environment in
which the first binding has already occurred, etc. In a LET-expression,
all the bindings are done in one environment.
For example, suppose the variable A is already bound to 10 when the
expression (LET ((A 20) (B A)) B) is evaluated. The result is 10, not 20,
because B is bound to A's value in the outer environment. In contrast,
(LET* ((A 20) (B A)) B) evaluates to 20.
(LET* ((var val ) (var val ) ... (var val )) . body)
1 1 2 2 n n
<=>
(LET ((var val ))
1 1
(LET ((var val ))
2 2
...
(LET ((var val )) . body)...))
n n
<=>
((LAMBDA (var )
1
((LAMBDA (var )
2
...
((LAMBDA (var ) . body)
n
val ) ...)
n
val ))
2
val )
1
-12-
(LABELS specs . body) -> value-of-body Special form
LABELS is useful for defining local procedures that may be mutually
recursive. Each spec should be of the form (variable value). The
value-expressions are evaluated (in no particular order), and the body is
evaluated in an environment where all the variables are bound to those
values. However, LABELS differs from LET in that the bindings of the
variables are already in effect (scope) before the value-expressions are
evaluated, so that the value-expressions can refer to any of the
variables, including the ones to which they themselves will be bound (thus
permitting local recursive procedures).
Each spec may alternatively have the form
((variable . argument-vars) . body).
This is equivalent to
(variable (LAMBDA argument-vars . body))
This is intended to parallel the syntax for DEFINE (see page 15).
In the following example, note that REV-1 is bound to a value that refers
to REV-1.
(DEFINE (REVERSE L)
(LABELS (((REV-1 L RESULT-SO-FAR)
(COND ((NULL? L) RESULT-SO-FAR)
(ELSE
(REV-1 (CDR L)
(CONS (CAR L) RESULT-SO-FAR))))))
(REV-1 L '())))
As an extension to the dialect of SCHEME described in [STEELE78REV], T
does not restrict the value expressions to be LAMBDA-expressions.
However, the values of the variables are not defined until body is
evaluated; i.e., they should not be used in the value-expressions. For
example,
(LABELS ((A B) (B 1)) . body)
is ill-formed; at the point when the expressions B and 1 are being
evaluated, the variable B is bound, but will have an undefined value.
Consequently, A will have an undefined value. By contrast, in the
well-formed expression
(LABELS ((A (LAMBDA (X) (+ X B))) (B 1)) . body),
which can also be written
(LABELS (((A X) (+ X B)) (B 1)) . body),
the value of B is not used before body is evaluated. The
LAMBDA-expression evaluates to a closure that includes B. By the time A
(the closure) is called, B will have a value.
-13-
LABELS cannot easily be described in terms of LAMBDA. However, it can be
described in terms of LAMBDA and SET. (SET performs side-effects on
bindings; see page 25.) This is misleading because LABELS should not be
thought of as a side-effecting operator.
(LABELS ((var val )
1 1
(var val )
2 2
...
(var val ))
n n
. body)
<=>
(LET ((var (UNDEFINED-VALUE))
1
(var (UNDEFINED-VALUE))
2
...
(var (UNDEFINED-VALUE)))
n
(SET var val )
1 1
(SET var val )
2 2
...
(SET var val )
n n
. body)
This equivalence is overly concrete in that the order of evaluation of the
val expressions, and the sequence in which the variables are given their
i
values, is not defined.
4.3. Locales
Locales serve two purposes. First, they provide an incremental or declarative
syntax for creating variable bindings in a contour. Second, they provide
access to environments as objects which can be dynamically manipulated. The
term locale is used to mean both the kind of contour introduced by a
LOCALE-expression, and the object which represents the environment.
There are no global variables in T; all variables are lexically bound in some
contour. Locales play the role of what is known in other Lisp and Scheme
dialects as the global environment.
(LOCALE variable . body) -> value-of-body Special form X8
Introduces a locale and evaluates body (an implicit block) in it. The
value of the LOCALE expression is that of body's last form. Within body,
variable is bound to the locale. If variable is (), then the locale is
not accessible as an object.
-14-
[LOCALE has been removed from T>3. Use MAKE-LOCALE, &c., instead.]
[(DECLARE ...) Reserved Word X8
In T>3 a new special form, DECLARE is introduced, but it's syntax has not
yet been released publicly.]
(DEFINE variable value) -> undefined Special form
(DEFINE (variable . arguments) . body) -> undefined
DEFINE creates a binding for variable in the lexically innermost locale in
which the DEFINE expression occurs. The variable is given value as its
value.
The second DEFINE syntax is for defining procedures.
(DEFINE (variable . arguments) . body)
is equivalent to
(DEFINE variable (LAMBDA arguments . body))
For example:
(DEFINE (F X) (LIST X 2)) <=> (DEFINE F (LAMBDA (X) (LIST X 2)
(DEFINE (F . X) (LIST X 2)) <=> (DEFINE F (LAMBDA X (LIST X 2)))
(LSET variable value) -> value Special form X6
LSET is identical to DEFINE except that the procedure definition syntax is
not permitted, and the variable is declared to be alterable. Variables
bound by DEFINE may not be altered with SET; variables bound by LSET may
be. See SET, page 25.
Any LOCALE-expression whose variable position is () may be rewritten as an
expression involving only LET or LABELS. In this case the use of LOCALE is
simply a notational difference; using LOCALE instead of LET or LABELS may make
code easier or harder to read and manipulate.
(LOCALE ()
(DEFINE FOO ...)
(DEFINE BAR ...)
...)
<=>
(LABELS ((FOO ...)
(BAR ...))
...)
This transformation may not be possible if the LOCALE-expression binds a
variable to the locale; in this case new bindings may be added as a result of
calls to *DEFINE or *LSET (see below), and there is no way to anticipate what
variables should be bound by the LABELS or LET expression.
-15-
References in a LOCALE body to variables bound later on in that locale are
undefined. For example,
(LET ((A 10)) (LOCALE () (LET ((B A)) (DEFINE A 20) B)))
has an undefined effect; it may yield 10 or 20, or it may signal an error.
Rationale: The undefinedness of forward references permits implementations
flexibility in dealing with the declarative nature of DEFINE and LSET.
New bindings may be registered either when encountered during evaluation,
to simplify evaluation, or may be registered during a pre-processing
(compilation) phase, to permit compilation to a more efficient
representation.
(MAKE-LOCALE superior-locale identification) -> locale
Creates a locale inferior to superior-locale. Identification may be any
object, and is used only for debugging purposes.
(DEFINE *FOO-ENV* (MAKE-LOCALE STANDARD-ENV '*FOO-ENV*))
(MAKE-EMPTY-LOCALE identification) -> locale
Creates a locale containing no bindings whatsoever.
(LOCALE? object) -> boolean Type predicate
Returns true if object is a locale.
4.4. Non-local reference
(*VALUE locale identifier) -> object Settable
Accesses the value of identifier in locale. If identifier has no value in
locale, then the effect of the call to *VALUE is undefined.
(*VALUE STANDARD-ENV 'T) => true
(*VALUE (LOCALE Z (DEFINE A 10) Z) 'A) => 10
(*DEFINE locale identifier value) -> undefined
Defines the value of identifier in locale to be value.
(*LSET locale identifier value) -> value
Creates a binding for identifier in locale with initial value value.
-16-
(IMPORT locale . variables) -> undefined
Locally defines variables to have the same values as their values in
locale.
(IMPORT locale var var ... var )
1 2 n
<=>
(BLOCK (DEFINE var (*VALUE locale 'var ))
1 1
(DEFINE var (*VALUE locale 'var ))
2 2
...
(DEFINE var (*VALUE locale 'var )))
n n
-17-
Chapter 5 Control
5.1. Conditionals
(COND . clauses) -> value-of-clause Special form
COND is a general conditional construct. Each clause should have one of
the following forms:
- (test . body)
- (test)
- (test => procedure)
The test expressions are evaluated in sequence until one yields true, at
which point:
- If it is part of a clause of the form (test . body), then body,
an implicit block, is evaluated, and its value is what the
COND-expression yields.
- If the clause has the form (test), then the COND-expression
yields the value of test (which of course is non-null).
- If the clause has the form (test => procedure), then the
procedure expression is evaluated, and its value, which should
be a procedure, is called with the value of the test as its one
and only argument. The COND yields the value of that call.
Example:
(COND ((PAIR? X) (GAUR (CAR X)))
((FIXNUM? X) (GAUR X)))
If all the tests yield false, then the COND yields an undefined value.
See section 2.4 for a discussion of undefined values.
The system variable ELSE has a non-null value. This provides a convenient
way to specify a branch to take when all other tests fail.
(COND ((PAIR? X) (GAUR (CAR X)))
((ASSQ X *SAMPLE-ALIST*) => CDR)
((GAUR X))
(ELSE NIL))
(XCOND . clauses) -> value-of-clause Special form
Similar to COND, but its effect is undefined (and presumably an error is
signalled) if no clause is selected. X is mnemonic for "exhaustive".
ELSE -> true
ELSE has as its value some true value. It exists mainly for use in COND.
-18-
(IF test consequent alternate) -> value-of-arm Special form
(IF test consequent) -> undefined
IF is a primitive two-way conditional. The test expression is evaluated.
If it yields true, then the consequent expression is evaluated, and the IF
yields its value. Otherwise, the alternate expression, if any, is
evaluated, and the IF yields its value. If test yields false and there is
no alternate, then the value of the IF is undefined.
(IF T 1 2) => 1
(IF (PAIR? 'X) 'FOO 'BAR) => BAR
(IF test consequent) <=> (IF test consequent (UNDEFINED-VALUE))
(CASE key . clauses) -> value-of-clause Special form
CASE performs a multi-way dispatch based on the value of the key
expression. Each clause should be of the form (keys . body), where keys
is a list of values (not expressions!) against which the value of the key
expression is compared (using EQ?), and body is an implicit block. The
body of the clause which matches the key value is evaluated, and the
CASE-expression yields the value of the body. The last clause may be of
the form (ELSE . body); this designates a default action to be taken if no
other clause is selected. If the key never matches and there is no
default clause, then the CASE-expression yields an undefined value.
(CASE 'B ((A) 'LOSE) ((B C) 'WIN) (ELSE NIL)) => WIN
(XCASE key . clauses) -> value-of-clause Special form
Similar to CASE, but no ELSE-clause is permitted, and the effect is
undefined (and presumably an error is signalled) if no clause is selected.
(SELECT key . clauses) -> value-of-clause Special form
SELECT is like CASE, but the car of each clause is a list of expressions
that are evaluated, instead of a list of constants.
(DEFINE *RED-COLOR* ...)
(DEFINE *GREEN-COLOR* ...)
(DEFINE *BLUE-COLOR* ...)
(SELECT COLOR
((*RED-COLOR* *BLUE-COLOR*) 'INORGANIC)
((*GREEN-COLOR*) 'ORGANIC))
(XSELECT key . clauses) -> value-of-clause Special form
Similar to SELECT, but no ELSE-clause is permitted, and the effect is
undefined (and presumably an error is signalled) if no clause is selected.
-19-
(NOT object) -> boolean Type predicate
(FALSE? object) -> boolean
NOT returns true if object is false, and returns false otherwise.
(NOT NIL) => true
(NOT T) => false
(NOT 3) => false
(AND . tests) -> value-of-test or false Special form
The test expressions are evaluated from left to right, until one evaluates
to false, at which point the AND yields false. If no test evaluates to
false then the value of the AND is the value of the last test.
(AND 3 4) => 4
(AND 3 NIL) => false
(AND) => true
(OR . tests) -> value-of-test or false Special form
The test expressions are evaluated from left to right, until one evaluates
to true (i.e., not null); its value is yielded as the value of the OR. If
no test evaluates to true, then the value of the OR is false.
(OR 3 4) => 3
(OR 3 NIL) => 3
(OR) => false
(*AND . objects) -> object
If any of the objects is false, *AND returns false; otherwise it returns
the last object. This is superficially similar to AND, but it is a
procedure, not a special form. That is, the value of *AND is a procedure,
whereas AND is a reserved word which introduces a special syntactic form.
This fact implies that (a) the variable *AND has a value suitable to be
passed as an argument to some procedure, and (b) a call to *AND behaves
differently from an AND special form in the case that the argument
expressions have side-effects. In
(AND NIL (FOO))
FOO will not be called, whereas in
(*AND NIL (FOO))
FOO will be called.
(*OR . objects) -> object
If any of the objects is true, *OR returns that object; otherwise it
returns false. *OR's relation to OR is analogous to *AND's relation to
AND.
-20-
(*IF test consequent alternate) -> object
If test is true, *IF returns consequent; otherwise it returns alternate.
*IF's relation to IF is analogous to *AND's relation to AND.
5.2. Iteration
Separate iteration constructs are not strictly necessary in T, since iteration
may be written using recursive procedures defined by LABELS (or even DEFINE).
There is no penalty for this in T, either in efficiency or practicality (for
example, stack size), because tail-recursions are implemented without net
growth of stack, and it is expected that compilers will implement local
procedures efficiently.
However, the following two iteration constructs provide a somewhat more
convenient notation for loops than does LABELS. (There are also some
higher-order procedures which perform iteration; see for example MAP, page 48,
and WALK, page 48.)
(DO specs (end-test . exit-forms) . body) -> value-of-exit Special form
Iterates until end-test yields true. Each spec should be of the form
(variable initial next). The variables are initialized (bound) in
parallel to the values of the initial-expressions, as with LET; then, the
end-test and body are executed iteratively. If the end-test yields true
on any iteration, then the loop terminates, at which point the exit-forms
are evaluated. The value of the DO-expression is the value of the last
exit-form, or the value of the end-test if there are no exit-forms (this
is like a COND clause). At the end of each iteration, the
next-expressions are all evaluated, and the variables are re-bound to the
values of the next-expressions.
DO is useful for loops which have a single termination condition, such as
numerical, ALGOL-style for-loops, loops where desired results are
accumulated in variables, and loops that perform side-effects on every
element in a sequence (see WALK, page 48).
(REVERSE L) <=>
(DO ((L L (CDR L))
(RESULT '() (CONS (CAR L) RESULT)))
((NULL? L) RESULT)))
(VECTOR-FILL VECTOR CONSTANT) <=>
(DO ((I (-1+ (VECTOR-LENGTH VECTOR)) (-1+ I)))
((<0? I) VECTOR)
(VSET VECTOR I CONSTANT))
Any DO-expression may be rewritten using the more primitive LABELS
construct. The following example has only one induction variable and one
body-expression, and assumes that there are no name conflicts with the
variable LOOP.
(DO ((variable initial next)) exit-clause body)
<=>
(LABELS (((LOOP variable)
(COND exit-clause
(ELSE body
(LOOP next)))))
(LOOP initial))
-21-
(ITERATE variable specs . body) -> value-of-body Special form
Another iteration construct. Specs has the form
((arg val ) (arg val ) ... (arg val ))
1 1 2 2 n n
The variable is bound to a local procedure whose formal parameters are
arg arg ... arg . The procedure is initially invoked with the vals
1 2 n
bound to the corresponding args.
ITERATE is more flexible than DO. It is useful in loops where the
terminating condition occurs in the middle of the body of the loop, or
where there is more than one terminating condition, or where the iteration
may occur in several places.
For example, consider the following example (from [STEELE78REV]), a
procedure to sort a list of trees into atoms and lists:
(DEFINE (COLLATE X)
(ITERATE COL ((Z X) (ATOMS '()) (LISTS '()))
(COND ((NULL? Z)
(LIST ATOMS LISTS))
((ATOM? (CAR Z))
(COL (CDR Z) (CONS (CAR Z) ATOMS) LISTS))
(ELSE
(COL (CDR Z) ATOMS (CONS (CAR Z) LISTS))))))
A rough analogy ITERATE : LABELS :: LET : LAMBDA holds. ITERATE expands
trivially into LABELS, but may be more convenient than LABELS for the same
reason that a LET form may be preferable than a call to a
LAMBDA-expression.
(ITERATE variable ((arg val ) (arg val ) ... (arg val )) . body
1 1 2 2 n n
<=>
(LABELS (((variable arg arg ... arg ) . body)) (variable val va
1 2 n 1
5.3. Procedure application
The only operation which all procedures support is invocation. All initial
system routines, like CAR, LIST, and EQ?, are procedures. Procedures are
usually created by evaluating LAMBDA- or DEFINE-forms.
The standard way to apply a procedure to arguments is with a call expression.
Calls are described on page 3.
(PROCEDURE? object) -> boolean Type predicate
Returns true if object is a procedure, i.e., if it may be called.
-22-
(APPLY procedure . objects) -> object
APPLY applies a procedure to a list of arguments.
The sequence of object arguments should have the form
arg arg ... arg arglist
1 2 n
where n may be 0. The procedure is called with arg , arg , etc., as the
1 2
first n arguments, and the members of arglist as final arguments.
(APPLY CONS '(1 2)) => (1 . 2)
(APPLY CONS 1 '(2)) => (1 . 2)
(APPLY CONS 1 2 '()) => (1 . 2)
(APPLY LIST 1 2 3 '(4 5 6)) => (1 2 3 4 5 6)
5.4. Sequencing
(BLOCK . body) -> value-of-body Special form
Body is a non-empty sequence of expressions. These expressions are
evaluated in order from left to right. The BLOCK-expression yields the
value of the last expression.
Because their values are ignored, expressions other than the last are
useful only for side-effects they may cause.
Since the bodies of most special forms are implicitly blocks, BLOCK is
useful only in places where syntactically a single expression is needed,
but some sequencing is required.
(BLOCK0 first-form . body) -> value-of-first-form Special form
The first-form is evaluated, then the expressions in the body are
evaluated. The BLOCK0-expression yields first-form's value.
5.5. Non-local exits
(CATCH variable . body) -> value-of-body Special form X5
CATCH provides a structured, non-local exit facility similar to PROG and
RETURN (without GO), or CATCH and THROW, in other Lisps. The variable is
bound to an escape procedure of one argument. If the escape procedure is
called as a result of evaluating body, then the argument to the escape
procedure is returned as the value of the CATCH-expression. If the escape
procedure is not called, then CATCH returns whatever body returns. A call
to an escape procedure is called a throw.
In the following example, the call to LIST never happens, because one of
its subforms performs a throw.
(CATCH X (LIST 1 (X 2) 3)) => 2
-23-
T's CATCH is a restriction of SCHEME's. Escape procedures are only valid
within the dynamic extent of the CATCH-expression; the CATCH can yield a
value at most once. Escape procedures should not be returned "upwards."
A throw may cause side-effects if the dynamic state in effect when the
escape procedure is called differs from that in effect when evaluation of
its corresponding CATCH-expression began. In this case the throw undoes
the effects of BIND forms, and must perform cleanup action requested by
UNWIND-PROTECT forms, in the process of "unwinding" the evaluation context
from that of the throw to that of the CATCH. See section 6.3 for
descriptions of BIND and UNWIND-PROTECT.
5.6. Lazy evaluation
Delays provide a general mechanism for delayed (lazy) evaluation, also known as
"call-by-need".
(DELAY expression) -> delay Special form
Returns a delay of the expression. The computation of the expression,
which happens at most once, is delayed until its value is requested with
FORCE.
(DEFINE (INF-LIST-OF-INTEGERS N)
(CONS N (DELAY (INF-LIST-OF-INTEGERS (1+ N)))))
(HEAD (TAIL (TAIL (INF-LIST-OF-INTEGERS 1)))) => 3
(DEFINE HEAD CAR)
(DEFINE (TAIL OBJ) (FORCE (CDR OBJ)))
The behavior of DELAY and FORCE can be described by the following
hypothetical implementation:
(DEFINE-SYNTAX (DELAY EXP)
`(MAKE-DELAYED-OBJECT (LAMBDA () ,EXP)))
(DEFINE (MAKE-DELAYED-OBJECT THUNK)
(LET ((FORCED-YET? NIL)
(FORCED-VALUE NIL))
(OBJECT NIL
((FORCE SELF)
(COND ((NOT FORCED-YET?)
(SET FORCED-VALUE (THUNK))
(SET FORCED-YET? T)))
FORCED-VALUE))))
(DEFINE-OPERATION (FORCE OBJ)
OBJ)
(FORCE delay) -> object
Forces the computation of the delay's value. If FORCE has not previously
been applied to the delay, then the expression in the original
DELAY-expression which created delay (see above) is evaluated, and its
value is returned. If FORCE has previously been applied to the delay,
then the value previously computed is returned.
If FORCE is applied to a non-delay, then it simply returns its argument.
-24-
Chapter 6 Side effects
A side-effect is a change in the state of a running T system, such as a change
in the value stored in some location, or an effect on the outside world.
Many useful programs can be written which make no use of side-effects. In
principle, side-effects other than those controlling the outside world are
unnecessary. However, side effects, when used carefully, can be used to
promote program modularity and readability. In addition, they may be necessary
in a T implementation to obtain efficient program execution.
6.1. Assignment
T provides several special forms which perform assignment to locations. A
location is a place in which a value may be stored, for example, a variable, or
the car of a list, or a component of a structure. Locations are not objects
per se, although references to them may be obtained through the use of
locatives.
(SET location new-value) -> new-value Special form X6
SET is a generalized assignment operator. It alters the location
specified by location to hold new-value. The value of any SET-expression
is the new-value thus stored.
If location is a symbol, then it names a variable which has been
previously bound by a LAMBDA or an LSET (or indirectly by any form which
does LAMBDA-binding, such as LET), whose value is to be altered.
(SET X (LIST 1 2 3)) => (1 2 3)
X => (1 2 3)
Location may also have the syntax of a call to an access-type procedure or
operation such as CAR. (In this manual, these are marked as Settable.)
(SET (CAR X) 'A) => A
X => (A 2 3)
(SET (proc arg ... arg ) value) <=> ((SETTER proc) arg ... arg value)
1 n 1 n
(SETTER procedure) -> procedure Operation
Given an access routine, SETTER returns a corresponding alteration
routine. (SETTER CAR), for example, evaluates to a primitive procedure
that takes two arguments, a list whose car is to be changed, and a value
to store in the car of the list.
See also DEFINE-SETTABLE-OPERATION, page 33.
(SWAP location new-value) -> object Special form
This behaves the same as SET, except that the value returned is the old
value found in location, rather than the new-value. For example, the
following expression exchanges the values of the variables X and Y:
(SET X (SWAP Y X))
-25-
(EXCHANGE location1 location2) -> undefined Special form
Exchanges the values in the two locations.
(MODIFY location procedure) -> object Special form X6
Stores a value in the location which is obtained by applying procedure to
the old value in location.
(MODIFY location 1+) <=> (INCREMENT location)
(MODIFY-LOCATION location continuation) -> object Special form
Calls continuation, which should be a procedure of two arguments, passing
it an access procedure of no arguments, which fetches the value in
location; and an update procedure of one argument, which will store a new
value in location.
MODIFY-LOCATION's purpose is to ensure that any subforms of the form for
location are evaluated only once. For example,
(INCREMENT (CAR (HACK))
<=>
(MODIFY-LOCATION (CAR (HACK))
(LAMBDA (FETCH STORE) (STORE (+ (FETCH) 1))))
In this expression, (HACK) will be evaluated only once, whereas in the
following naive expansion
(SET (CAR (HACK)) (+ (CAR (HACK)) 1))
it is evaluated twice, which is presumably neither necessary nor
desirable. The expansion of MODIFY-LOCATION forms introduces extra
temporary variables, as needed, to account for this.
Although awkward when written directly, MODIFY-LOCATION is useful in macro
expansions. MODIFY-LOCATION is the common special form in terms of which
other "modification" forms such as PUSH, MODIFY, and SWAP are
implementeded as macros.
For example, one might define a DOUBLE macro, which multiplies by two the
value in a location, as follows:
(DEFINE-SYNTAX (DOUBLE FORM)
`(MODIFY-LOCATION
,FORM
(LAMBDA (FETCH STORE) (STORE (* (FETCH) 2)))))
(DOUBLE (CAR (HACK)))
-26-
6.2. Locatives
Locatives in T are similar to the pointers or references of languages like C or
Algol 68.
Locatives are obtained by evaluating LOCATIVE-expressions. The value in the
location they refer to may be fetched using the CONTENTS operation, and stored
using SET.
Objects which act as locatives may be created using OBJECT, as long as they
handle the CONTENTS and (SETTER CONTENTS) operations appropriately, and handle
LOCATIVE? by returning true.
(LOCATIVE location) -> locative Special form
Returns a locative which accesses the location specified by location.
Location receives similar treatment to the location position in the SET
special form; it may refer to a variable, or to a "field" within some
mutable structure, for example the car of a pair.
The following equivalence approximates the behavior of LOCATIVE:
(LOCATIVE location)
<=>
(OBJECT NIL
((CONTENTS SELF) location)
(((SETTER CONTENTS) SELF VALUE) (SET location VALUE))
((LOCATIVE? SELF) T))
This is accurate if location is a variable, but if it is a call, the
subforms of the call are evaluated when the LOCATIVE-expression is
evaluated, not when the contents of the locative are required. For
example,
(LOCATIVE (FOO (BAR BAZ)))
<=>
(LET ((TEMP1 FOO)
(TEMP2 (BAR BAZ)))
(OBJECT NIL
((CONTENTS SELF) (TEMP1 TEMP2))
(((SETTER CONTENTS) SELF VALUE) (SET (TEMP1 TEMP2) VALUE
((LOCATIVE? SELF) T)))
(CONTENTS locative) -> object Settable operation
Dereferences (indirects through) the locative.
(LSET Z (LIST 'A 'B)) => (A B)
(CONTENTS (LOCATIVE (CAR Z))) => A
(SET (CONTENTS (LOCATIVE (CAR Z))) 'C) => C
Z => (C B)
(CONTENTS (LOCATIVE location)) <=> location
(SET (CONTENTS (LOCATIVE location)) value <=> (SET location valu
(LOCATIVE? object) -> boolean Type predicate operation
Returns true if object is a locative.
-27-
6.3. Dynamic state
(BIND specs . body) -> value-of-body Special form
BIND implements dynamic binding. Syntactically, it is similar to LET, but
semantically it is completely different. BIND operates by doing temporary
assignments to locations, for example, to bound variables. Unlike LET, it
does not introduce a new lexical binding contour.
Note that the use of the term bind in this context is unrelated to its use
elsewhere (for example, at the beginning of chapter 4.1).
Each spec should have the syntax
(location value)
The value in each location is obtained and saved. Each location is
assigned the value, as with SET. The body expressions are evaluated and
the value of the last is saved. The locations are then restored to their
original saved values. The BIND-expression finally yields the saved value
of the body.
(BIND ((location value)) . body)
is therefore similar to
(LET ((SAVED-VALUE location))
(SET location value)
(BLOCK0 (BLOCK . body)
(SET location SAVED-VALUE)))
or
(LET ((SAVED-VALUE location))
(SET location value)
(UNWIND-PROTECT (BLOCK . body)
(SET location SAVED-VALUE)))
Examples:
(LSET A 1)
(DEFINE (F) (+ A 1))
(F) => 2
(LET ((A 10)) (F)) => 2
(BIND ((A 10)) (F)) => 11
(LSET X (LIST 1 2 3))
(BIND (((CAR X) 10))
(LSET Y (COPY-LIST X)))
X => (1 2 3)
Y => (10 2 3)
The values in the locations are restored, even if a throw out of body
occurs (see CATCH, page 23). For example:
(LET ((A 10))
(CATCH EP (BIND ((A 20)) (EP NIL)))
A)
=> 10
Any location in a BIND-form which is a variable name should name a
variable which has been previously bound using, for example, LSET or LET.
-28-
(UNWIND-PROTECT form . unwind-forms) -> value-of-form Special form
UNWIND-PROTECT behaves nearly the same as BLOCK0: the form is evaluated,
and the value is saved; the unwind-forms are evaluated; and the
UNWIND-PROTECT-expression yields the (saved) value of form. However,
unlike BLOCK0, UNWIND-PROTECT guarantees that the unwind-forms will be
evaluated, even if a throw occurs during the evaluation of form.
In the following example, the motor will be stopped even if DRILL-HOLE
attempts to throw out of the UNWIND-PROTECT.
(UNWIND-PROTECT (BLOCK (START-MOTOR)
(DRILL-HOLE))
(STOP-MOTOR))
A throw (see page 23) out of the BLOCK -- for example, as a result of
calling DRILL-HOLE, will evaluate the exit form (STOP-MOTOR) before
control finally returns to its corresponding CATCH.
It is an error to throw out of the unwind forms of an UNWIND-PROTECT.
-29-
Chapter 7 Operations
T provides a simple mechanism for programming in what has come to be known as
the object-oriented style of programming. Object-oriented style may be
contrasted to what will here be called procedural style. In object-oriented
programming, programs are organized by giving the behavior of objects when
various operations are applied to them, whereas in procedural style, programs
are organized by giving the behavior of procedures when applied to various
kinds of objects. These styles are equivalent in terms of computational power,
but each offers different advantages in terms of readability and modularity.
T supports the object-oriented style with a special kind of procedure known as
an operation, and with forms which permit one to create objects which exhibit
certain behavior when a given operation is applied to them.
When an operation is called, the following sequence of events occurs:
- A handler is obtained for the object which was the operation's first
argument. (Operations must always be called with at least one
argument.)
- The operation asks the handler for a method which will handle the
operation for the object.
- The handler either provides a method, or it indicates that the object
is not prepared to handle the operation.
- If the handler provided a method, the method is invoked. If not,
then the operation's default method, if any, is invoked. If the
operation has no default method, then the effect of the call to the
operation is undefined (and presumably an error condition is
signalled).
In this way, an object's handler may determine how the operation is to be
performed for the object -- that is, which particular method is to be invoked
as a result of invoking the operation.
Handlers map operations to methods. Many objects may have the same handler, or
a handler may be idiosyncratic to a particular object. However, every object
has a handler, so all objects participate in the generic operation dispatch
protocol.
7.1. Fundamental forms
The basis of the generic operation system consists of the two special forms
OBJECT and OPERATION. OBJECT-expressions create objects which respond
appropriately to generic operations, and OPERATION-expressions evaluate to
operations.
-30-
(OBJECT procedure . method-clauses) -> object Special form
An OBJECT-expression yields an object which is prepared to handle generic
operations according to the method-clauses. In the following description,
"the object" refers to the value of a given OBJECT-expression.
Each method-clause should be of the form
((operation . variables) . body)
Operation is an evaluated position, and is typically a variable which
evaluates to an operation, although it may be any expression. When an
operation is called with the object as its first argument, the
operation-expressions are evaluated, and if one yields the operation being
applied to the object, the corresponding method-clause is selected. The
operation is then performed according to the selected method-clause: the
clause's variables are bound to the arguments to the operation, and its
body, an implicit block, is evaluated.
(DEFINE OP (OPERATION NIL))
(OP (OBJECT NIL ((OP SELF) 34))) => 34
(OP (OBJECT NIL ((OP SELF X) X)) 55) => 55
Procedure may be any expression, and is evaluated at the time the
OBJECT-expression is evaluated. The object, when called, simply calls the
value of the procedure expression, passing on any arguments. Typically
procedure might be either a LAMBDA-expression, if the object is to be
callable, or it is NIL, which by convention means that the object is not
intended to be called, the value of NIL being an uncallable object.
In the degenerate case, where there are no method clauses, the value of
(OBJECT (LAMBDA args . body))
is indistinguishable from that of
(LAMBDA args . body).
The semantics of the OBJECT and OPERATION special forms can be described
in terms of hypothetical primitive procedures *OBJECT and GET-HANDLER.
These primitives do not actually exist in T, but are introduced here as
expository aids. *OBJECT takes two arguments, and returns an object
which, when called, calls the object which was *OBJECT's first argument,
and when given to GET-HANDLER returns the object which was *OBJECT's
second argument. That is, *OBJECT creates a two-component record (like a
pair), GET-HANDLER extracts one component, and the other component is
called when the record is called.
(GET-HANDLER (*OBJECT proc handler)) <=> handler
((*OBJECT proc handler) arg ...) <=> (proc arg ...)
In addition, GET-HANDLER is defined on all objects to return some handler,
even objects not created by *OBJECT (if indeed there are any such
objects).
-31-
Given these primitives, the following rough equivalence holds:
(OBJECT proc
((op . args ) . body )
1 1 1
((op . args ) . body )
2 2 2
...
((op . args ) . body ))
n n n
<=>
(*OBJECT proc
(LAMBDA (OP)
(SELECT OP
((op ) (LAMBDA args . body ))
1 1 1
((op ) (LAMBDA args . body ))
2 2 2
...
((op ) (LAMBDA args . body ))
n n n
(ELSE NIL))))
The outer LAMBDA-expression yields the object's handler; the inner
LAMBDA-expressions yield the methods, and the mapping from operations to
methods is accomplished by the SELECT-expression (see SELECT, page 21).
Note that the syntactic positions op , op , ... op are evaluated
1 2 n
positions, and the operation expressions are evaluated when an operation
is applied to the object, not when the object is created.
(OPERATION default . method-clauses) -> operation Special form
The syntax of OPERATION is the same as that of OBJECT, but its semantics
and application are somewhat different. An OPERATION-expression evaluates
to an operation. When called, the operation obtains a handler for its
first argument, calls the handler to obtain a method, and then invokes the
method. The default method for the operation is established as being
default.
As the subject of another generic operation, an operation is an object
like any other, and in this case the operation acts just as if it had been
created by an OBJECT-expression with the same method-clauses. In this way
one can establish the behavior of an operation when subject to other
operations, for example SETTER.
The following rough equivalence describes the semantics of OPERATION.
Some details have been omitted.
(OPERATION default . methods)
<=>
(LABELS ((OP (OBJECT (LAMBDA (OBJ . ARGS)
(LET ((METHOD ((GET-HANDLER OBJ) OP)))
(COND (METHOD
(APPLY METHOD OBJ ARGS))
(ELSE
(APPLY default OBJ ARGS)))))
. methods)))
OP)
-32-
For example:
(DEFINE OP (OPERATION (LAMBDA (OBJ) 'ZEBU)))
(OP (OBJECT NIL ((OP SELF) 'QUAGGA))) => QUAGGA
(OP 'ELAND) => ZEBU
An operation is created, and the variable OP is bound to it. The
operation's default method always returns the symbol ZEBU. When the
operation is applied to the value of the OBJECT-expression, the
appropriate method is invoked, and the call to the operation yields the
symbol QUAGGA. When the operation is applied to an object which doesn't
handle it -- the symbol ELAND -- the operation's default method is
invoked, so the call yields the symbol ZEBU.
(OPERATION? object) -> boolean Type predicate
Returns true if object is an operation.
7.2. Defining operations
(DEFINE-OPERATION (variable . argument-vars) . body) -> undefined Special form
Defines variable to be an operation. The syntax is intended to be
analogous to that of DEFINE. The operation's default method is defined by
argument-vars and body. If there is no body, then the operation's default
method is undefined. In this case, the argument-vars appear only for
documentary purposes.
(DEFINE-OPERATION (var . args) . body)
<=> (DEFINE var (OPERATION (LAMBDA args . body)))
(DEFINE-OPERATION (var . args))
<=> (DEFINE var (OPERATION UNDEFINED-EFFECT))
(DEFINE-SETTABLE-OPERATION (variable . argument-vars) . body) -> undefinedSpeci
Defines variable to be an operation, as with DEFINE-OPERATION, but
arranges for the operation's "setter" to be another operation, so that the
operation is "settable" (see page 28).
(DEFINE-SETTABLE-OPERATION (var . args) . body)
<=>
(DEFINE var
(LET ((THE-SETTER (OPERATION UNDEFINED-EFFECT)))
(OPERATION (LAMBDA args . body)
((SETTER SELF) THE-SETTER))))
-33-
(DEFINE-PREDICATE variable) -> undefined Special form
Defines variable to be an operation which, by default, returns false.
(DEFINE-PREDICATE var)
<=>
(DEFINE-OPERATION (var OBJ) NIL)
The intent is that particular OBJECT-expressions contain clauses of the
form ((variable SELF) T). This way the operation defined by
DEFINE-PREDICATE may act as a type predicate that returns true only for
those objects returned by such OBJECT-expressions.
7.3. Joined objects
(JOIN . objects) -> joined-object X7
JOIN returns an object, called a joined object, whose behavior is a
combination of the behaviors of the objects. When an operation is applied
to the joined object, each object in turn is given an opportunity to
handle the operation; the first to handle it does so.
(JOIN (OBJECT proc method method ...)
1 11 12
(OBJECT proc method method ...))
2 21 22
<=>
(OBJECT proc method method ... method method ...)
1 11 12 21 22
Using the hypothetical primitives described earlier, JOIN could be defined
by the following:
(JOIN first second)
<=>
(*OBJECT first
(LAMBDA (OP)
(OR ((GET-HANDLER first) OP)
((GET-HANDLER second) OP))))
[In T>3, JOIN works on procedures and objects created by OBJECT, but
does not yet work on structures or primitive objects.]
7.4. Synonyms
Synonyms are objects whose behavior under generic operations may vary
dynamically.
(SYNONYM expression) -> synonym Special form X7
Yields a synonym for expression. If an operation is applied to the
synonym, the expression is evaluated, yielding another object to which the
operation will then be applied.
Again using the non-existent *OBJECT and GET-HANDLER primitives:
(SYNONYM exp)
<=>
(LET ((THUNK (LAMBDA () exp)))
(*OBJECT (LAMBDA ARGS (APPLY (THUNK) ARGS))
(LAMBDA (OP) ((GET-HANDLER (THUNK)) OP))))
[SYNONYM has been removed from T>3.]
-34-
7.5. Example
Hypothetical implementation of CONS:
(DEFINE (CONS THE-CAR THE-CDR)
(OBJECT NIL
((PAIR? SELF) T)
((CAR SELF) THE-CAR)
((CDR SELF) THE-CDR)
(((SETTER CAR) SELF NEW-CAR) (SET THE-CAR NEW-CAR))
(((SETTER CDR) SELF NEW-CDR) (SET THE-CDR NEW-CDR))))
(DEFINE-PREDICATE PAIR?)
(DEFINE-SETTABLE-OPERATION (CAR PAIR))
(DEFINE-SETTABLE-OPERATION (CDR PAIR))
-35-
Chapter 8 Numbers
Numbers in T are objects which represent real numbers. Numbers come in three
varieties: integers, ratios, and floating point numbers. Integers and ratios
are exact models of the corresponding mathematical objects. Floating point
numbers give discrete approximations to real numbers within a certain
implementation-dependent range.
As expressions, numbers are self-evaluating literals (see section 3.3).
-72723460248141 => -72723460248141
13/5 => 13/5
34.55 => 34.55
There may be many different objects which all represent the same number. The
effect of this is that EQ? may behave in nonintuitive ways when applied to
numbers. It is guaranteed that if two numbers have different types or
different numerical values, then they are not EQ?, but two numbers that appear
to be the same may or may not be EQ?, even though there is no other way to
distinguish them. Use EQUAL? or = (page 39) for comparing numbers.
8.1. Predicates
(NUMBER? object) -> boolean Type predicate
Returns true if object is a number.
(RATIONAL? object) -> boolean Type predicate
Returns true if object is an integer or a ratio.
(INTEGER? object) -> boolean Type predicate
Returns true if object is an integer.
(FLOAT? object) -> boolean Type predicate
Returns true if object is a floating point number.
(RATIO? object) -> boolean Type predicate
Returns true if object is a ratio.
(ODD? integer) -> boolean
Returns true if integer is odd.
(EVEN? integer) -> boolean
Returns true if integer is even.
-36-
8.2. Arithmetic
(+ . numbers) -> number
(ADD . numbers) -> number
Returns the sum of the numbers.
(+ 10 27) => 37
(+ 10 27.8) => 37.8
(+) => 0
(+ -35) => -35
(+ 1 2 3) => 6
(- n1 n2) -> number
(SUBTRACT n1 n2) -> number
Returns the difference of n1 and n2.
(- n) -> number
(NEGATE n) -> number
Returns the additive inverse of n.
(* . numbers) -> number
(MULTIPLY . numbers) -> number
Returns the product of the numbers.
(/ n1 n2) -> number
(DIVIDE n1 n2) -> number
Returns the quotient of n1 and n2.
(/ 20 5) => 4
(/ 5 20) => 1/4
(/ 5.0 20) => 0.25
(QUOTIENT n1 n2) -> integer
Integer division; truncates the quotient of n1 and n2 towards zero.
(REMAINDER n1 n2) -> number
Returns the remainder of the division of n1 by n2, with the sign of n1.
Bug: REMAINDER in Tau 2.7 does strange things if its arguments aren't
integers.
-37-
(MOD n1 n2) -> number
Returns a number k in the range from zero, inclusive, to n2, exclusive,
such that k differs from n1 by an integral multiple of n2. If n1 is
positive, this behaves like REMAINDER.
Bug: MOD in Tau 2.7 does strange things if its arguments aren't
integers.
(EXPT base exponent) -> number
Returns base raised to the exponent power. Base and exponent may be any
numbers.
Bug: In Tau 2.7, exponent must be an integer.
(ABS n) -> number
Returns the absolute value of n.
(GCD integer1 integer2) -> integer
Returns the greatest common divisor of integer1 and integer2.
(GCD 36 60) => 12
(ADD1 n) -> number
(1+ n) -> number
Returns n + 1. (See also INCREMENT, page 42.)
(SUBTRACT1 n) -> number
(-1+ n) -> number
Returns n - 1. (See also DECREMENT, page 42.)
(MIN . numbers) -> number
Returns the number with the least numerical magnitude. (There must be at
least one number.)
(MAX . numbers) -> number
Returns the number with the greatest numerical magnitude. (There must be
at least one number.)
(TRUNCATE number) -> number
Truncates towards zero.
-38-
8.3. Comparison
(= n1 n2) -> boolean
(EQUAL? n1 n2) -> boolean
Returns true if n1 is numerically equal to n2.
(< n1 n2) -> boolean
(LESS? n1 n2) -> boolean
Returns true if n1 is numerically less than n2.
(> n1 n2) -> boolean
(GREATER? n1 n2) -> boolean
Returns true if n1 is numerically greater than n2.
(N= n1 n2) -> boolean
(NOT-EQUAL? n1 n2) -> boolean
Returns true if n1 is not numerically equal to n2.
(>= n1 n2) -> boolean
(NOT-LESS? n1 n2) -> boolean
Returns true if n1 is not numerically less than n2.
(<= n1 n2) -> boolean
(NOT-GREATER? n1 n2) -> boolean
Returns true if n1 is not numerically greater than n2.
8.4. Sign predicates
(ZERO? n) -> boolean
(=0? n) -> boolean
Returns true if n is numerically equal to zero.
(NEGATIVE? n) -> boolean
(<0? n) -> boolean
Returns true if n is negative.
(POSITIVE? n) -> boolean
(>0? n) -> boolean
Returns true if n is positive.
-39-
(NOT-ZERO? n) -> boolean
(N=0? n) -> boolean
Returns true if n is not numerically equal to zero.
(NOT-NEGATIVE? n) -> boolean
(>=0? n) -> boolean
Returns true if n is non-negative.
(NOT-POSITIVE? n) -> boolean
(<=0? n) -> boolean
Returns true if n is non-positive.
8.5. Transcendental functions
(EXP float) -> float
x
Exponential function (e ).
(LOG float) -> float
Natural logarithm (log x).
e
(SQRT float) -> float
Returns the square root of its argument.
(COS float) -> float
Returns the cosine of its argument.
(SIN float) -> float
Returns the sine of its argument.
(TAN float) -> float
Returns the tangent of its argument.
(ACOS float1) -> float2
Arccosine. Returns a number whose cosine is float1.
(ASIN float1) -> float2
Arcsine. Returns a number whose sine is float1.
-40-
(ATAN2 x y) -> float
Two-argument arctangent. This computes the angle between the positive
x-axis and the point (x, y). For example, if (x, y) is in the first or
third quadrant, (ATAN2 x y) returns the arctangent of y/x.
Bug: ATAN2 isn't implemented in Tau 2.7. Tau 2.7 does implement ATAN
(arctangent), however.
8.6. Bitwise logical operators
Bug: In Tau 2.7, the routines in this section are restricted to take
fixnums (see page 123), not arbitrary integers.
(LOGAND integer1 integer2) -> integer
Returns the bitwise logical and of its arguments.
(LOGAND 10 12) => 8
(LOGIOR integer1 integer2) -> integer
Returns the bitwise logical inclusive or of its arguments.
(LOGXOR integer1 integer2) -> integer
Returns the bitwise logical exclusive or of its arguments.
(LOGNOT integer) -> integer
Returns the bitwise logical not of its argument.
(ASH integer amount) -> integer
Shifts integer to the left amount bit positions. If amount is negative,
shifts integer right by the absolute value of amount bit positions.
(BIT-FIELD integer position size) -> integer
Extracts a bit field out of integer. The field extracted begins at bit
position position from the low-order bit of the integer and consists of
size bits. The extracted field is returned as a positive integer.
(BIT-FIELD 27 1 3) => 5
(BIT-FIELD -1 4 5) => 31
(SET-BIT-FIELD integer position size value) -> integer
Inserts a value into a bit field of integer. The field begins at bit
position position from the low-order bit of the integer and consists of
size bits. A new integer is returned which is the same as the argument
integer except that this field has been altered to be value.
(SET-BIT-FIELD 32 1 3 5) => 42
-41-
8.7. Coercion
(->INTEGER number) -> integer
Coerces number to an integer, truncating towards zero if necessary.
(->INTEGER 17) => 17
(->INTEGER 7/4) => 1
(->INTEGER 17.6) => 17
(->INTEGER -17.6) => -17
(->FLOAT number) -> float
Coerces number to a floating point number.
(->FLOAT 17) => 17.0
(->FLOAT 7/4) => 1.75
(->FLOAT 17.6) => 17.6
8.8. Assignment
INCREMENT and DECREMENT are assignment forms, like SET and PUSH. See section
6.1 for a general discussion.
(INCREMENT location) -> number Special form
Adds one to the value in location, stores the sum back into location, and
yields the sum.
(LSET L 6)
(INCREMENT L) => 7
L => 7
(LSET M (LIST 10 20 30)) => (10 20 30)
(INCREMENT (CAR M)) => 11
M => (11 20 30)
(INCREMENT location) <=> (MODIFY location ADD1)
(DECREMENT location) -> number Special form
Subtracts one from the value in location, stores the difference back into
location, and yields the difference.
(DECREMENT location) <=> (MODIFY location SUBTRACT1)
-42-
Chapter 9 Lists
This section describes routines available for creating, examining, and
otherwise manipulating list structure.
9.1. Predicates
(NULL? object) -> boolean Type predicate
Returns true if object is the empty list (null). Since the empty list is
also used for the logical false value, and non-false objects are
considered to be true, it turns out that NULL? is the same as NOT.
(PAIR? object) -> boolean Type predicate
Returns true if object is a pair.
(PAIR? '(A B C)) => true
(PAIR? '(A . 15)) => true
(PAIR? '()) => false
(PAIR? 'FOO) => false
(ATOM? object) -> boolean Type predicate
Returns true if object is an atom. An atom is any object that isn't a
pair.
(ATOM? object) <=> (NOT (PAIR? object))
(LIST? object) -> boolean Type predicate
Returns true if object is either a pair or the empty list.
(LIST? '(A B C)) => true
(LIST? '(A . 15)) => true
(LIST? '()) => true
(LIST? 'FOO) => false
(PROPER-LIST? object) -> boolean
Returns true if list is a properly formed list, i.e., if its last pair's
cdr is null.
(PROPER-LIST? '(A B C)) => true
(PROPER-LIST? '(A B . C)) => false
(PROPER-LIST? '()) => true
(NULL-LIST? list) -> boolean
Like NULL?, but has an undefined effect (and may, for example, signal an
error) if its argument is not either a pair or null. (NULL-LIST? x) is
roughly the same as (NULL? (PROCLAIM LIST? x)).
-43-
9.2. Constructors
(CONS object1 object2) -> pair
Returns a new pair whose car is object1 and whose cdr is object2.
(CONS 'A '()) => (A)
(CONS 'A 'B) => (A . B)
(CONS 'A '(B)) => (A B)
(CONS 'A (CONS 'B '())) => (A B)
(CONS 'A (LIST 'B 'C)) => (A B C)
(LIST . objects) -> list
Returns a new list of its arguments.
(LIST) => ()
(LIST 'A) => (A)
(LIST 'A 'B) => (A B)
(LIST 'A (LIST 'B 'C)) => (A (B C))
LIST <=> (LAMBDA THINGS THINGS)
(CONS* . objects) -> pair
CONS* is a generalized CONS. It returns a new, possibly improper, list,
by consing the initial objects onto the last object.
(CONS* 1 2 3) => (1 2 . 3)
(CONS* 'A 'B '(C D)) => (A B C D)
(CONS* object) <=> object
(CONS* object1 object2)
(COPY-LIST list) -> list
Makes a "top-level" copy of a list; that is, returns a new list whose
elements are the same as the original.
(COPY-LIST list) <=>
(COND ((NULL-LIST? list) '())
(ELSE (CONS (CAR list) (COPY-LIST (CDR list)))))
(COPY-LIST list) <=> (MAP IDENTITY list)
9.3. List access
(CAR pair) -> object Settable X7
Returns the car of the pair. By special dispensation, (CAR '()) => (),
even though the empty list is not a pair. [(CAR '()) is an error in T >3.]
(CDR pair) -> object Settable X7
Returns the cdr of the pair. By special dispensation, (CDR '()) => (),
even though the empty list is not a pair. [(CDR '()_) is an error in T>3.]
-44-
(C...R pair) -> object Settable
Compositions of CAR and CDR, up to four deep, are defined.
(CADAR x) <=> (CAR (CDR (CAR x)))
(CADDDR x) <=> (CAR (CDR (CDR (CDR x))))
(NTH list n) -> object Settable
th
Returns the n element of list. The first element (the car) is
(NTH list 0), the second element is (NTH list 1), and so on. In general,
list must have at least n+1 elements.
(NTH '(A B) 0) => A
(NTH '(A B) 1) => B
(NTH '(A B) 2) has an undefined effect
(NTH '() n) has an undefined effect for any n
(NTHCDR list n) -> list Settable
th
Returns the n tail of list. In general, list must have at least n
tails.
(NTHCDR '(A B) 0) => (A B)
(NTHCDR '(A B) 1) => (B)
(NTHCDR '(A B) 2) => ()
(NTHCDR '(A B) 3) has an undefined effect
(NTHCDR '() n) has an undefined effect for any nonzero n
Bug: NTHCDR doesn't handle SETTER (i.e. is not settable) in Tau 2.7.
(LAST list) -> object Settable
Returns the last element of list.
(LAST '(A B C)) => C
(LAST list) <=> (CAR (LASTCDR list))
(LASTCDR list) -> pair Settable
Returns the last pair in list.
(LASTCDR '(A B C)) => (C)
(LASTCDR '(A B . C)) => (B . C)
Bug: LASTCDR doesn't handle SETTER (i.e. is not settable) in Tau 2.7.
-45-
9.4. Lists as sequences
(LENGTH list) -> integer
Return the length of list.
(LENGTH '()) => 0
(LENGTH '(A B C)) => 3
(APPEND . lists) -> list
Constructs a list which is the concatenation of lists. A new list is
constructed, except that the result has the last non-null argument as a
tail.
(APPEND '(A B C) '(D E) '(F G)) => (A B C D E F G)
(APPEND! . lists) -> list
Destructive version of APPEND. Splices the lists together, setting the
cdr of the last pair in the first list to the second list, and so on.
Returns its first non-null argument.
(DEFINE L1 (LIST 'A 'B 'C))
(DEFINE L2 (LIST 'D 'E))
L1 => (A B C)
(APPEND! L1 L2) => (A B C D E)
L1 => (A B C D E)
(APPEND! '() L2) => (D E)
(REVERSE list) -> list
Returns a new list whose elements are those of list, in reverse order.
(REVERSE '(A B C)) => (C B A)
(REVERSE! list) -> list
This is a "destructive" version of REVERSE; it allocates no storage for
the result, but rather recycles the pairs in the source list's to form the
result list.
(SUBLIST list start count) -> list
Returns a list of count elements of list, beginning with element start.
(SUBLIST '(A B C D E F) 2 3) => (C D E)
-46-
9.5. Lists as sets
(MEMQ? object list) -> boolean
Returns true if object is an element of list.
(MEMQ? 'B '(A B C)) => true
(MEMQ? 'B '(A (B C) D)) => false
(MEMQ? 'B '(B)) => true
(MEMQ? 'B '()) => false
(MEMQ? 17 '(10 17)) => undefined
(MEMQ? "foo" '("foo" "bar")) => undefined
(LET ((X "foo"))
(MEMQ? X (LIST 'B X))) => true
(MEM? predicate object list) -> boolean
Returns true if list contains an object which is equal to object according
to predicate, which should be an equality predicate.
(MEM? = 17 '(10 17)) => true
(MEM? STRING-EQUAL? "foo" '("foo" "bar")) => true
(ANY? predicate . lists) -> boolean
Returns true if any element of list answers true to predicate.
(ANY? NUMBER? '(FOO 15 BAR)) => true
(EVERY? predicate . lists) -> boolean
Returns true if every element of list answers true to predicate.
(EVERY? SYMBOL? '(A B C)) => true
(DELQ object list) -> list
Returns a list which is the same as the argument list with all occurrences
of object removed.
(DEL predicate object list) -> list
Returns a list which is the same as list except that all elements which,
according to predicate, are equal to object, have been removed. Predicate
should be an equality predicate.
(DELQ object list) <=> (DEL EQ? object list)
(DELQ! object list) -> list
Destructive version of DELQ. Removes all occurrences of object from list,
possibly altering it, and returns the new and/or modified list.
-47-
(DEL! predicate object list) -> list
Destructive version of DEL.
(DELQ! object list) <=> (DEL! EQ? object list)
9.6. Mapping Procedures
(MAP proc . lists) -> list
Returns a list of the results of applying proc to successive elements of
th
the lists; the n element of the result list is the result of calling
th
proc on the n elements of the lists. The length of the result list is
the same as the length of the shortest of the lists.
(MAP (LAMBDA (X) (LIST 'A X)) '(CAT DOG)) => ((A CAT) (A DOG))
(MAP + '(10 20) '(5 6)) => (15 26)
(MAP LIST '(A B) '(C D E) '(F)) => ((A C F))
(MAPCDR proc . lists) -> list
MAPCDR is similar to MAP, except that proc is applied to successive tails
of the lists.
(MAP! proc list) -> list
For each pair in list, applies proc to the car of the pair, and then
modifies the pair so that its car is the value of the application.
(DEFINE L (LIST 'CAT 'DOG)) => (CAT DOG)
(MAP! (LAMBDA (X) (LIST 'A X)) L)
L => ((A CAT) (A DOG))
(WALK proc . lists) -> undefined
This is similar to MAP but the result is a value of no particular
interest. Thus WALK is useful only for the side-effects that proc
performs.
(WALKCDR proc . lists) -> undefined
This is similar to MAPCDR but the result is a value of no particular
interest.
-48-
9.7. Lists as associations
(ASSQ object list) -> pair or false
(ASSQ object list) <=> (ASS EQ? object list)
(ASS predicate object list) -> pair or false
An association list is a list of pairs that represents a "lookup table"
where the car of each pair matches a lookup key. ASS and its related
procedures search association lists by comparing (applying predicate) the
key (object) against the car of each element of list (i.e., the caar of
each tail). If it finds a match, it returns that element (the car of the
tail). If it finds no match, it returns false.
(ASS EQ? 'B '((A 10 20) (B 30 40) (C 50 60))) => (B 30 40)
(ASS = 9 '((1 Y) (4 Y) (9 Z) (12 P))) => (9 Z)
(ASS = 10 '((1 Y) (4 Y) (9 Z) (12 P))) => false
9.8. Lists as stacks
PUSH and POP permit a convenient syntax for the use of lists in implementing
simple stacks. They are assignment forms, like SET and INCREMENT. See section
6.1 for a general discussion of assignment forms.
(PUSH location object) -> undefined Special form
Cons a pair whose car is object and whose cdr is the value in location
(usually a list), and store the extended list back into location.
(LSET L '(34 55)) => (34 55)
(PUSH L 21)
L => (21 34 55)
(PUSH L '(A B C))
(PUSH (CAR L) 'D)
L => ((D A B C) 21 34 55)
In general,
(PUSH location object)
<=> (SET location (CONS object location))
<=> (MODIFY location (LAMBDA (L) (CONS object L)))
(POP location) -> object Special form
The car of the value in location (this value should be a pair) is
returned. As a side-effect, the cdr of the list is stored back in the
location.
L => (21 34 55)
(POP L) => 21
L => (34 55)
In general,
(POP location) <=> (BLOCK0 (CAR location) (SET location (CDR loc
<=> (BLOCK0 (CAR location) (MODIFY location C
-49-
Chapter 10 Trees
This chapter describes procedures available for manipulating trees. The term
tree is used in a technical sense to refer to non-circular list structure
considered as tree data-structures, as contrasted with lists, which use chained
pairs to represent a one-dimensional sequence of objects. Thus one might say
either that (A (B C) D) is a list of three elements, or that it is a tree with
four (non-null) leaves.
Trees are used to represent programs. The tree-manipulation procedures and
special forms are designed with this in mind.
-50-
10.1. Comparison
(EQUIV? object1 object2) -> boolean
EQUIV? is an equality predicate, not for comparing trees but for comparing
leaves.
- If (EQ? object1 object2), then (EQUIV? object1 object2).
- If object1 and object2 are both numbers of the same type, then
they are EQUIV? iff they have the same numeric value (see =,
page 39).
- If object1 and object2 are both strings, then they are EQUIV? if
they are STRING-EQUAL? (page 62).
See also the descriptions of EQ? (page 9) and = (page 29).
Bug: In Tau 2.7, EQUIV? (and ALIKEV?) may return false for two
numbers which are =. It will do the right thing, however, for
28
integers which are less than 2 in magnitude.
(ALIKE? predicate tree1 tree2) -> boolean
Returns true if tree1 and tree2 have the same shape and their
corresponding leaves are equal according to predicate, which must be an
equality predicate.
(ALIKE? = '(1 (2 . 8) 3) '(1 (2 . 8) 3)) => true
(ALIKEQ? tree1 tree2) -> boolean
(ALIKEQ? tree1 tree2) <=> (ALIKE? EQ? tree1 tree2)
(ALIKEV? tree1 tree2) -> boolean
(ALIKEV? tree1 tree2) <=> (ALIKE? EQUIV? tree1 tree2)
Roughly speaking, two trees are ALIKEV? if they print the same way.
10.2. Tree utilities
(SUBST predicate new old tree) -> tree
Returns a result tree which is the same as the argument tree, except that
leaves in the argument tree which, according to predicate, are equal to
old, have been changed to be new. Predicate must be an equality
predicate.
(SUBST EQUIV? 17 13 '(10 (20 13) 30)) => (10 (20 17) 30)
The result returned by SUBST may or may not share structure with its tree
argument.
-51-
(SUBSTQ new old tree) -> tree
(SUBSTQ new old tree) <=> (SUBST EQ? new old tree)
(SUBSTV new old tree) -> tree
(SUBSTV new old tree) <=> (SUBST EQUIV? new old tree)
(COPY-TREE tree) -> tree
Recursively make a copy of the tree.
(COPY-TREE tree) <=> (SUBSTQ NIL NIL tree)
(TREE-HASH tree) -> integer
Compute a hash code for tree. The hash computed is a non-negative fixnum
with the property that if tree1 and tree2 are ALIKEV?, then their hashes
are the same.
10.3. Destructuring
(DESTRUCTURE specs . body) -> value-of-body Special form
Specs has the form ((pattern value) (pattern value) ...) where each
pattern is either an identifier (e.g., X) or a tree, all of whoe non-null
leaves are identifiers (e.g., ((X Y . Z) P Q)).
DESTRUCTURE is similar to LET, and in the case that all the patterns are
symbols, DESTRUCTURE and LET are equivalent. But DESTRUCTURE is especially
useful when one wants to bind variables to the various nodes in a single
tree structure. For example, suppose Z has the value
(1 (2 3) ((4 5 . 6) 7)), and we want to bind A to 1, B to 2, C to (3), D
to 6, and E to 7. We could write
(LET ((A (CAR Z))
(B (CAADR Z))
(C (CDADR Z))
(D (CDDAR (CADDR Z)))
(E (CADADR (CDR Z))))
...)
However, we could also write
(DESTRUCTURE (((A (B . C) ((() () . D) E)) Z))
...)
The ()'s notate ignored positions.
-52-
(DESTRUCTURE* specs . body) -> value-of-body Special form
DESTRUCTURE* is the "serial" form of DESTRUCTURE, just as LET* is the
"serial" form of LET.
(DESTRUCTURE* ((pattern value )
1 1
(pattern value )
2 2
...
(pattern value ))
n n
code)
<=>
(DESTRUCTURE ((pattern value ))
1 1
(DESTRUCTURE ((pattern value ))
1 1
...
(DESTRUCTURE ((pattern value ))
n n
code )...))
10.4. Quasiquote
T's quasiquote facility, inherited from the Maclisp family of Lisps, is useful
for building programs whose form is determined, but where some of the structure
is constant and some is to be filled in. This is especially convenient in the
definitions of macro expanders. It is so useful that a special external syntax
is provided for notating such templates. It takes its name from the name of
the character (quasiquote, `) which introduces this special syntax.
Note that it behaves like QUOTE except one can specify that subforms of the
quasiquoted form should be evaluated. Inside a form that is preceded by a
quasiquote, two additional pieces of external syntax are active: comma (,), and
comma at-sign (,@); these are the ways to indicate that some subform should be
evaluated (i.e., unquoted). A subform preceded by a comma is evaluated and
replaced by the result of the evaluation. A subform preceded by a comma
at-sign is evaluated and replaced by splicing in the result of the evaluation.
Here are some simple examples. Note that the first example shows code
equivalence, not evaluation.
`(A B ,X Y) <=> (LIST 'A 'B X 'Y)
(DEFINE X '(3))
(CDR `(A B ,X C)) => (B (3) C)
(CDR `(A B ,@X C)) => (B 3 C)
(DEFINE-SYNTAX (REPEAT N . CODE) ;Execute CODE N times
`(LET ((COUNT ,N) (THUNK (LAMBDA () ,@CODE)))
(DO ((COUNT COUNT (-1+ COUNT)))
((<=0? COUNT) '())
(THUNK))))
-53-
It is possible to nest quasiquoted forms. This is useful when writing complex
source-to-source transformations.
(DEFINE-SYNTAX (FOO X Y)
`(LET ((STUFF ,X))
`((STUFF-IS ,STUFF)
(Y-IS ,',Y))))
(FOO (+ 3 5) BAZ) => ((STUFF-IS 8) (Y-IS BAZ))
[In T>3, quasiquote works on vectors as well, thus `#(1 2 ,(+ 1 2)) -> #(1 2 3)]
-54-
Chapter 11 Structures X11
A structure corresponds to what is called a record in some other languages: a
data structure with a fixed set of named fields or slots, known in T as
components. For example, one might want an "employee" structure, with slots
for the employee's name, age, and salary. A structure, then, is an organized
set of locations, places to store things.
11.1. Terminology
Every structure has an associated an object called a structure type (sometimes
abbreviated stype, pronounced "ess type"). Structure types are created by the
procedure MAKE-STYPE (see below) and by the special form DEFINE-STRUCTURE-TYPE
Given a structure type S, one can access several things of interest:
Constructor a procedure that will create uninitialized structures of type S
(e.g., the structure for a particular employee).
Predicator a procedure (a type predicate) that returns true if its
argument is a structure of type S.
Component selectors
procedures that access the various components of structures of
type S (one selector per component).
Handler the mechanism for specifying how a set of generic operations
will behave when applied to structures of type S.
11.2. Defining structure types
The DEFINE-STRUCTURE-TYPE macro is a convenient way to define a new structure
type and its associated constructor, predicator, and component selectors.
DEFINE-STRUCTURE-TYPE is a macro defined in terms of MAKE-STYPE and the other
low-level routines described in the next section. One may want to define one's
own interface to these routines, one that initializes the components when a new
instance is constructed, for example.
(DEFINE-STRUCTURE-TYPE typename . componentnames) -> stype Special form
Creates a structure type (using MAKE-STYPE), and defines a number of
variables:
- typename-STYPE is defined to be the structure type.
- MAKE-typename is defined to be the constructor.
- typename? is defined to be the predicator.
- typename-componentname is defined to be a component selector,
for each of the componentnames.
-55-
For example,
(DEFINE-STRUCTURE-TYPE EMPLOYEE NAME AGE SALARY)
defines
- EMPLOYEE-STYPE, a variable whose value is the structure type
whose name is EMPLOYEE.
- (MAKE-EMPLOYEE), a procedure that creates uninitialized
EMPLOYEE-structures.
- (EMPLOYEE? object), a type predicate.
- (EMPLOYEE-NAME employee)
(EMPLOYEE-AGE employee)
(EMPLOYEE-SALARY employee), selectors that access the components
of EMPLOYEE-structures.
11.3. Manipulating structure types
(MAKE-STYPE typename componentnames) -> stype
Returns a new structure type. The typename, which should be a symbol, is
used for printing and identification purposes only. Componentnames should
be a list of symbols. The componentnames correspond to the components
that instances of the structure will have.
For example:
(MAKE-STYPE 'EMPLOYEE '(NAME AGE SALARY)) => #{Stype EMPLOYEE}
This creates a structure type, identified as EMPLOYEE, whose instances
have components NAME, AGE, and SALARY. The object returned, a structure
type, is appropriate as an argument to other routines described in this
section.
(STYPE-ID stype) -> typename
Returns the type identifier for stype.
(STYPE-CONSTRUCTOR stype) -> procedure
Given a structure type stype, returns the procedure which will instantiate
(i.e., create instances of) the structure type. The constructor procedure
takes no arguments and creates a structure with uninitialized components.
Note that the constructor procedure is created at the time the structure
type is created, not at the time that STYPE-CONSTRUCTOR is called.
((STYPE-CONSTRUCTOR stype)) <=> (COPY-STRUCTURE (STYPE-MASTER st
-56-
(STYPE-MASTER stype) -> structure
Returns the "master copy" from which all structures created by the
structure type's constructor-procedure are made. This can be convenient
for establishing default values for components in newly created
structures. The way to do this is by storing into the master copy the
desired default values; these values will be copied into all new instances
of the structure type. For example,
(SET (EMPLOYEE-SALARY (STYPE-MASTER EMPLOYEE-STYPE)) 12000)
(EMPLOYEE-SALARY (MAKE-EMPLOYEE)) => 12000
(STYPE-PREDICATOR stype) -> procedure
Given a structure type stype, returns the procedure (a type predicate)
which will predicate membership in the structure type. This procedure
takes one argument and returns true if that argument is an instance of the
structure type.
Note that the predicator procedure is created at the time the structure
type is created, not at the time that STYPE-PREDICATOR is called.
(STYPE-SELECTOR stype componentname) -> procedure
Given a structure type stype, returns the procedure which will select the
component of the structure type's instances identified by componentname.
The selector procedure takes one argument, which must be a structure whose
structure type is stype, and returns the appropriate component of the
instance.
Such selector procedures support SETTER operations, which is to say that
one may alter components of structures by using SET or related special
forms.
The effect of selecting a structure component that has not been previously
set, or initialized from the master structure (see STYPE-MASTER, above),
is undefined.
Note that the selector procedures are created at the time the structure
type is created, not at the time that STYPE-SELECTOR is invoked.
(STYPE-SELECTORS stype) -> list of procedures
Given a structure type stype, returns a list of its selector procedures.
(SELECTOR-ID selector) -> identifier
Given a selector procedure, e.g., as returned by STYPE-SELECTOR, returns
its corresponding componentname.
-57-
(STYPE-HANDLER stype) -> handler Settable
Given a structure type stype, accesses the handler to be used for generic
operations applied to instances of the structure type. For a newly
created structure type, this handler handles no operations, but one may
set the handler to be any handler at all.
11.4. Manipulating structures
The primary means for manipulating structures is to call their selector
procedures and their setters. For example, in the context of the examples
above, if EMPLOYEE-23 is an EMPLOYEE-structure, then EMPLOYEE-23's NAME
component may be retrieved by evaluating
(EMPLOYEE-NAME EMPLOYEE-23)
and this value may be set by evaluating
(SET (EMPLOYEE-NAME EMPLOYEE-23) 'FRED)
This works because the selector procedures for EMPLOYEE-STYPE handle the
generic operation called SETTER. Structures may also be manipulated using
other generic operations.
(STRUCTURE? object) -> boolean Type predicate
Returns true if object is a structure.
(COPY-STRUCTURE structure) -> structure
Makes a copy of structure. The value returned is a new object of the same
structure type as structure whose components are the same (in the EQ?
sense) as structure's.
(COPY-STRUCTURE! destination source) -> structure
Copies the components of source into destination, which is returned.
Source and destination must be structures of the same type.
-59-
Chapter 12 Characters and strings
T has a special data type for representing characters. Characters are objects
which may be stored in strings and communicated between the T system and
external media such as files and terminals. Most characters represent printed
graphics such as letters, digits, and punctuation.
The external syntax #\x is used for characters. x may either be a single
character or the "name" of a character. Valid character names include SPACE,
TAB, FORM, and NEWLINE. For example:
#\b The alphabetic character lower-case b
#\7 The digit 7
#\; The special character semicolon
#\tab The tab character
#\NEWLINE The new-line character
Some graphic characters are also readable by name:
#\LEFT-PAREN <=> #\(
#\RIGHT-PAREN <=> #\)
#\LEFT-BRACKET <=> #\[
#\RIGHT-BRACKET <=> #\]
#\LEFT-BRACE <=> #\{
#\RIGHT-BRACE <=> #\}
#\BACKSLASH <=> #\\
#\QUOTE <=> #\'
#\QUASIQUOTE <=> #\`
#\DOUBLEQUOTE <=> #\"
#\COMMA <=> #\,
#\SEMICOLON <=> #\;
The syntax #[Ascii n] may also be used for characters,where n is the ASCII code
for the character (see section 12.9). This is not preferred, however, since it
is less readable and less abstract than the #\ syntax.
#[Ascii 65] <=> #\A
Unlike numbers, characters are uniquely instantiated. There is only one object
which represents a given graphic or other character.
(EQ? #\x #\x) -> true
Characters and strings are self-evaluating. There is no need to quote them to
use them as constants in programs.
Strings are sequences of characters. Strings actually consist of two distinct
components, a header and a text, which may be manipulated independently of each
other. If one doesn't use the routines in section 12.6, one need not even be
aware of this fact, and can treat strings as if they are similar to lists of
characters.
-60-
Strings are notated simply by enclosing the actual sequence of characters
within double quote characters. For example,
"Horse"
notates a five-character string consisting of the characters #\H, #\o, #\r,
#\s, and #\e. The escape character (also known as backslash: \) may be used to
include a double-quote or a backslash within a string:
"The \"second\" word in this string is enclosed in double-quotes."
"\\ This string begins with one backslash."
There is no standard way to notate a string which contains non-graphic
characters (e.g. control characters).
Strings are not uniquely instantiated; e.g.
(EQ? "Eland" "Eland")
may or may not yield true, depending on the implementation.
12.1. Predicates
(CHAR? object) -> boolean Type predicate
Returns true if object is a character.
(CHAR? #\X) => true
(STRING? object) -> boolean Type predicate
Returns true if object is a string.
(STRING? "Tapir.") => true
(GRAPHIC? character) -> boolean
Returns true if character is either the space character (#\SPACE) or it
corresponds to a printed graphic such as a letter, digit, or punctuation
mark.
(GRAPHIC? #\X) => true
(GRAPHIC? #\NEWLINE) => false
(WHITESPACE? character) -> boolean
Returns true if character is a whitespace character (blank, tab, newline,
carriage return, line feed, or form feed).
(WHITESPACE? #\X) => false
(WHITESPACE? #\NEWLINE) => true
-61-
(ALPHABETIC? character) -> boolean
Returns true if character is an alphabetic (upper or lower case)
character.
(ALPHABETIC? #\y) => true
(ALPHABETIC? #\7) => false
(UPPERCASE? character) -> boolean
Returns true if character is an upper-case letter.
(UPPERCASE? #\y) => false
(UPPERCASE? #\Y) => true
(UPPERCASE? #\COMMA) => false
(LOWERCASE? character) -> boolean
Returns true if character is a lower-case letter.
(LOWERCASE? #\y) => true
(LOWERCASE? #\Y) => false
(LOWERCASE? #\COMMA) => false
(DIGIT? character radix) -> boolean
Returns true if character is a digit with respect to the given radix.
(DIGIT? #\5 10) => true
(DIGIT? #\a 10) => false
(DIGIT? #\a 16) => true
12.2. Comparison
(CHAR= char1 char2) -> boolean
(CHAR< char1 char2) -> boolean
(CHAR> char1 char2) -> boolean
(CHARN= char1 char2) -> boolean
(CHAR>= char1 char2) -> boolean
(CHAR<= char1 char2) -> boolean
Six comparison predicates are defined for characters. CHAR= and CHARN=
are defined for all characters. The others are defined only when the
arguments are both upper-case letters, or both lower-case letters, or both
digits.
(STRING-EQUAL? string1 string2) -> boolean
Returns true if the two strings have the same length and characters.
-62-
12.3. String constructors
(MAKE-STRING length) -> string
Makes a string of null characters whose length is length.
(STRING-APPEND . strings) -> string
Returns a new string which is the concatenation of strings.
(STRING-APPEND "llama" " and " "alpaca") => "llama and alpaca"
(COPY-STRING string) -> string
Returns a new string, with new text, whose characters and length are the
same as those of string.
(CHAR->STRING character) -> string
Creates a string of length one whose single element is character.
(CHAR->STRING #\B) => "B"
(LIST->STRING list) -> string
Converts a list of characters to a string.
(LIST->STRING '(#\Z #\e #\b #\u)) => "Zebu"
(STRING->LIST string) -> list
Converts a string to a list of characters.
(STRING->LIST "Zebu") => (#\Z #\e #\b #\u)
12.4. String access
(STRING-LENGTH string) -> integer Settable
Returns string's length. A string's length may be SET, but the new length
must be less than or equal to the original length.
(STRING-EMPTY? string) -> boolean
Returns true if string is an empty string.
(STRING-EMPTY? "") => true
(STRING-EMPTY? "Bharal") => false
(STRING-EMPTY? string) <=> (=0? (STRING-LENGTH string))
-63-
(STRING-ELT string n) -> character Settable
(NTHCHAR string n) -> character
th
Returns the n character in string (zero-based).
(NTHCHAR "SAIGA" 2) => #\I
(STRING-HEAD string) -> character Settable
(CHAR string) -> character
Returns first character in string.
(STRING-TAIL string) -> string
(CHDR string) -> string
Returns the "tail" of string.
(STRING-TAIL "Ibex.") => "bex."
(STRING-NTHTAIL string n) -> string
(NTHCHDR string n) -> string
th
Returns the n tail of string.
(NTHCHDR "SAIGA" 2) => "IGA"
(SUBSTRING string start count) -> string
th
Returns a substring of string, beginning with the start character, for a
length of count characters.
(SUBSTRING "A small oryx" 2 5) => "small"
(STRING-SLICE string start count) -> string
th
Returns a substring (slice) of string, beginning with the start
character, for a length of count characters.
(STRING-SLICE "A small oryx" 2 5) => "small"
Unlike SUBSTRING, the characters returned by STRING-SLICE are shared with
the original string; that is, any changes to characters in the original
string which have been selected in the substring are reflected in the
substring, and vice versa.
-64-
12.5. String manipulation
(STRING-POSQ character string) -> integer or false
Returns the index of the first occurrence of character in string, if it is
there; otherwise returns false.
(STRING-POSQ #\i "oribi") => 2
(STRING-POSQ #\s "oribi") => false
(STRING-REPLACE destination source count) -> string
Copies count characters from the source string to the destination string,
destructively, and return the modified destination.
(DEFINE S (COPY-STRING "The bison"))
(STRING-REPLACE S "Any how" 3) => "Any bison"
(MAP-STRING procedure string) -> string
Calls procedure on each character in string, collecting the successive
return values which should be characters in a new string.
(MAP-STRING CHAR-UPCASE "A grisbok") => "A GRISBOK"
(MAP-STRING! procedure string) -> string
Calls procedure on each character in string, storing the results which
should be characters back into string.
(WALK-STRING procedure string) -> undefined
Calls procedure on each character in string.
12.6. String header manipulation
A string header is a structure of fixed size which contains a pointer into a
string text, and a length. A string text is a vector of characters themselves.
The string text is not itself a directly accessible object, but can only be
manipulated via a string header. Several string headers may point into the
same text. The term string is used to refer to a header and text considered as
a whole.
(CHOPY string) -> string
Makes a new string header pointing to the same string text, and with the
same length, as the header for string.
(CHOPY! destination source) -> string
Copies the header for the source string into the header for the
destination string, which is returned.
-65-
(STRING-TAIL! string) -> string
(CHDR! string) -> string
Destructively modifies string's header to point to the next character in
its text, and decrements its length.
(LET ((S (COPY-STRING "String.")))
(CHDR! S)
S)
=>
"tring."
(STRING-NTHTAIL! string n) -> string
(NTHCHDR! string n) -> string
Destructive version of STRING-NTHTAIL.
12.7. Case conversion
(CHAR-UPCASE character) -> character
If character is a lower-case character, returns the corresponding
upper-case character. Otherwise character, which must be a character, is
returned.
(CHAR-DOWNCASE character) -> character
If character is an upper-case character, returns the corresponding
lower-case character. Otherwise character, which must be a character, is
returned.
(STRING-UPCASE string) -> string
Returns a copy of string with all lower-case characters converted to upper
case.
(STRING-UPCASE string) <=> (MAP-STRING CHAR-UPCASE string)
(STRING-DOWNCASE string) -> string
Returns a copy of string with all upper-case characters converted to lower
case.
(STRING-DOWNCASE string) <=> (MAP-STRING CHAR-DOWNCASE string)
(STRING-UPCASE! string) -> string
Destructive version of STRING-UPCASE.
(STRING-UPCASE! string) <=> (MAP-STRING! CHAR-UPCASE string)
-66-
(STRING-DOWNCASE! string) -> string
Destructive version of STRING-DOWNCASE.
(STRING-DOWNCASE! string) <=> (MAP-STRING! CHAR-DOWNCASE string)
12.8. Digit conversion
(CHAR->DIGIT character radix) -> integer
Returns the weight of the character when treated as a digit. Character
must be a digit in the given radix.
(CHAR->DIGIT #\A 16) => 10
(DIGIT->CHAR integer radix) -> character
Given a non-negative integer less than radix, returns a character (a
digit) whose weight is the integer.
(DIGIT->CHAR 10 16) => #\A
(DIGIT character radix) -> integer or false
If character is a digit, returns its weight; otherwise returns false.
(DIGIT #\5 10) => 5
12.9. ASCII conversion
(CHAR->ASCII character) -> integer
Given a character, returns its ASCII representation as an integer.
(ASCII->CHAR integer) -> character
Given an integer which is the ASCII code for some character, returns the
character.
NUMBER-OF-CHAR-CODES -> integer
The value of NUMBER-OF-CHAR-CODES is a number that is 1 larger than the
largest value that will ever be returned by CHAR->ASCII. This may be used
to make tables which are to be indexed by ASCII codes.
(DEFINE *TABLE* (MAKE-VECTOR *NUMBER-OF-CHAR-CODES*)) => vector
(VSET *TABLE* (CHAR->ASCII #\F) 'COW) => COW
(VREF *TABLE* (CHAR->ASCII #\F)) => COW
-67-
12.10. Symbols
Symbols are similar to strings, but are instantiated uniquely; only one symbol
with a given print name exists. Symbols are used to identify variables, among
other things. The fact that they have a convenient external representation
makes them useful for many purposes.
Symbols may be coerced to strings and vice versa. If two strings are equal to
each other (e.g. according to STRING-EQUAL?), then they will both convert to
the same symbol.
(EQ? (STRING->SYMBOL string1) (STRING->SYMBOL string2))
if and only if
(STRING-EQUAL? string1 string2)
See also sections 3.4 and 14.1.
(STRING->SYMBOL string) -> symbol
Returns the symbol whose print name is equal to string.
(STRING->SYMBOL "COW") => COW
(STRING->SYMBOL "123") => \123
(STRING->SYMBOL "bison") => \b\i\s\o\n
(STRING->SYMBOL "") => #[Symbol ""]
Note that it is READ-OBJECT (page 77), not STRING->SYMBOL, which coerces
alphabetic characters to upper case.
(SYMBOL->STRING symbol) -> string
Returns a string for which STRING->SYMBOL will return symbol.
(SYMBOL->STRING 'COW) => "COW"
-68-
Chapter 13 Miscellaneous features
13.1. Comments and declarations
(COMMENT . comment) -> undefined Special form
Does nothing and returns no value of interest. COMMENT-expressions may be
used to write comments in code. (The preferred way to write comments,
however, is with the semicolon read macro character.)
(IGNORE . variables) -> undefined Special form
Ordinarily, programs such as compilers which manipulate source programs
consider it to be an exceptional condition when a bound variable is not
referenced, and may generate warning messages when they detect this
condition. IGNORE-expressions may be used to suppress such warnings and
also to request that a warning be issued if in fact there are any
references to variables.
(LAMBDA (X Y) (IGNORE X) (CAR Y))
(IGNORABLE . variables) -> undefined Special form
IGNORABLE is like IGNORE except that permission is not given to give
warnings if any of variables actually is referenced. This is not useful
for human-generated expressions, but may be useful in the expansion of a
macro invocation where the macro expander may not know whether a bound
variable in the expansion is referenced or not, and wants to declare that
it is all right if the variable is not referenced.
13.2. Errors and dead ends
(ERROR control-string . arguments) -> object
Signals an error. Control-string and arguments should be arguments
suitable for a call to FORMAT (page 92). The error is reported in an
implementation-dependent manner, and an opportunity is provided to
possibly correct or proceed from the error. (See section 19.1 for details
of Tau's handling of errors.)
(ERROR "cannot wash the dishes because ~A" EXCUSE-DESCRIPTION-STRI
(SYNTAX-ERROR control-string . arguments) -> object
Similar to ERROR, but signals a syntax error. This should be called, for
example, from within macro expanders when an illegal syntax is
encountered.
(READ-ERROR port control-string . arguments) -> object
Similar to ERROR, but signals a read error. This should be called, for
example, from within read macros when an illegal read syntax is
encountered.
-69-
(CHECK-ARG predicate object procedure) -> object
Verifies that an object is of a particular type. Predicate should be a
type predicate, and object can be any object. If predicate, when applied
to object, returns false, then an error is signalled. If it returns true,
then the CHECK-ARG returns object. Procedure is a procedure whose name
will be given in any message printed by the error system.
The user interface (see section 19.1) may provide a way to supply a value
to use in place of object. If the user attempts to correct the error in
this way, then CHECK-ARG again verifies that the new value answers true to
predicate, and returns it.
For example:
(DEFINE (WASH-DISH DISH)
(LET ((DISH (CHECK-ARG DISH? DISH WASH-DISH)))
...))
(PROCLAIM predicate object) -> object
Returns object, which must answer true to predicate. A call to PROCLAIM
serves as a type declaration; this may assist an optimizing compiler in
generating efficient code.
(PROCLAIM predicate object)
<=>
(LET ((VALUE object))
(COND ((predicate VALUE) VALUE)
(ELSE (UNDEFINED-EFFECT))))
(ASSERT boolean) -> undefined
Has no effect and returns an undefined value, unless boolean is false, in
which case the effect is undefined (normally, this means that an error is
signalled).
(UNDEFINED-VALUE . arguments) -> undefined
Has no effect and yields some undefined value. An implementation will
endeavor to return some object which, when printed or otherwise displayed,
will show the arguments. This feature may be useful in debugging, for
example in tracking down the origin of the undefined value, if the value
has propagated to an undesirable place.
(UNDEFINED-EFFECT . arguments) -> undefined
The effect of calling UNDEFINED-EFFECT is undefined. An implementation
will endeavor to signal an error condition if such a call ever occurs;
however, an optimizing compiler may make use of the fact that the control
path leading to a call to UNDEFINED-EFFECT will never be taken in a
correctly running program, and so in some cases may eliminate the call.
-70-
13.3. Early binding
(DEFINE-CONSTANT variable value) -> undefined Special form
This is semantically identical to DEFINE, but also declares that the value
of the variable will not change. This might permit a compiler to perform
constant-folding. Also, if the variable is defined to be a small integer,
this may interact well with SELECT to obtain fast numeric dispatch on
"enumerated types."
(DEFINE-INTEGRABLE variable value) -> undefined Special form
(DEFINE-INTEGRABLE (variable . arguments) . body) -> undefined
This is semantically identical to DEFINE, but also declares that the value
of the variable is not expected to change. For example, if a reference to
the variable is encountered in functional position in a call, its
definition may be integrated, that is, substituted in-line.
Bug: TC 1.4 will blow up on integrating a recursive procedure defined
with DEFINE-INTEGRABLE.
13.4. Symbol generators
(GENERATE-SYMBOL prefix) -> symbol
Each call to GENERATE-SYMBOL generates a unique identifier. The form
which the new identifier's name takes is not defined, but the identifier
is guaranteed to be different from any identifier created in any other
way. The identifier's external representation will begin with prefix.
(CONCATENATE-SYMBOL . things) -> symbol
Creates a symbol whose print name is obtained by appending the printed
representations of things according to the DISPLAY operation.
(CONCATENATE-SYMBOL 'FOO- "THING-" 34) => FOO-THING-34
13.5. Combinators
(ALWAYS value) -> procedure
Returns a procedure which ignores its arguments and always returns value.
(ALWAYS value) <=> (LAMBDA X (IGNORE X) value)
(IDENTITY object) -> object
Identity function. Returns its argument.
-71-
(PROJN n) -> procedure
th
Returns a procedure which returns (projects) its n argument, ignoring
any others.
(PROJN 1) <=> (LAMBDA (A B . REST) (IGNORE A REST) B)
(PROJ0 object . rest) -> object
th
Projection function: returns its zero argument, ignoring the rest.
PROJ0 <=> (PROJN 0)
PROJ0 is similar to BLOCK0 (page23), except that it is a procedure, not a
special form.
(PROJ1 object0 object1 . rest) -> object1
Returns its second argument.
(PROJ2 object0 object1 object2 . rest) -> object2
Returns its third argument.
(PROJ3 object0 object1 object2 object3 . rest) -> object3
Returns its fourth argument.
(CONJOIN . predicates) -> predicate
Returns a predicate which is the logical conjunction of all of the
predicates.
((CONJOIN >0? ODD?) 13) => true
((CONJOIN >0? ODD?) 8) => false
(DISJOIN . predicates) -> predicate
Returns a predicate which is the logical disjunction of all of the
predicates.
((DISJOIN >0? ODD?) 13) => true
((DISJOIN >0? ODD?) 8) => true
(COMPLEMENT predicate) -> predicate
Returns a predicate which is the logical complement of the predicate.
ATOM? <=> (COMPLEMENT PAIR?)
((COMPLEMENT MEMQ?) 'A '(X Y Z)) => true
-72-
(COMPOSE . procedures) -> procedure
Returns a procedure which is the composition of the procedures. The last
of the procedures may take any number of arguments, and the resulting
procedure will take that same number of arguments; all the other
procedures must take one argument.
((COMPOSE CAR CDR) '(A B)) => B
(COMPLEMENT predicate) <=> (COMPOSE NOT predicate)
PROPER-LIST? <=> (DISJOIN NULL? (COMPOSE NULL? CDR LASTCDR))
NTH <=> (COMPOSE CAR NTHCDR)
(TRUE . arguments) -> true Type predicate
Ignores its arguments, and always returns true. This may be used as a
predicate representing the "universal type" -- the type which subsumes all
objects.
(FALSE . arguments) -> false Type predicate
Ignores its arguments, and always returns false. This may be used as a
predicate representing the "null type" -- the type which subsumes no
objects.
(TRUE? value) -> boolean Type predicate
Returns true if value is some true value, false otherwise. This is
convenient where one wants to coerce a truth value to be a standard truth
value; that is, TRUE? maps false to itself, and any true value to the
standard true value (T).
(TRUE? object) <=> (NOT (FALSE? object))
(TRUE? NIL) => false
(TRUE? T) => true
(TRUE? 3) => true
(BOOLEAN? object) -> boolean Type predicate
This returns true if object is either the standard true value or the
standard false value.
(BOOLEAN? NIL) => true
(BOOLEAN? T) => true
(BOOLEAN? 3) => false
13.6. Vectors
Vectors can be thought of as one-dimensional, zero-based arrays, or as
fixed-length, random-access lists. They read and print like lists with a # in
front. Like lists, but unlike numbers and strings, vectors are not
self-evaluating. To write a constant vector, quote it: '#(X Y (1 2)).
(VECTOR? object) -> boolean Type predicate
Returns true if object is a vector.
-73-
(MAKE-VECTOR size) -> vector
Returns a vector whose length is size. The elements are not initialized
to any particular value. VECTOR-FILL (see below) may be used to fill the
vector with some useful value (such as ()).
(LIST->VECTOR list) -> vector
Converts a list to a vector.
(LIST->VECTOR '(A B C)) => #(A B C)
(VECTOR->LIST vector) -> list
Converts a vector to a list.
(VECTOR->LIST '#(A B C)) => (A B C)
(VECTOR-ELT vector n) -> object Settable
(VREF vector n) -> object
th
Accesses the n element of vector (zero-based).
(VECTOR-ELT '#(A B C) 1) => B
(VSET vector n object) -> object
th
Sets the n element of vector to object.
(VSET vector n object) <=> (SET (VREF vector n) object)
(COPY-VECTOR vector) -> vector
Makes a copy of vector.
(VECTOR-FILL vector value) -> vector
Sets every element of vector to value, and returns the modified vector.
(VECTOR-REPLACE target source n) -> vector
Sets the first n elements of target to be the same as the corresponding
elements of source, and returns the (modified) target.
(VECTOR-LENGTH vector) -> integer
Returns vector's length.
-74-
(VECTOR-POS predicate object vector) -> integer or false
Returns index of the first element x of vector such that (predicate object
x), or false if there is no such element.
(VECTOR-POSQ object vector) -> integer or false
(VECTOR-POSQ object vector) <=> (VECTOR-POS EQ? object vector)
(WALK-VECTOR procedure vector) -> undefined
Applies procedure to every element in vector.
13.7. Pools
Pools give a convenient way to perform explicit storage management in T. One
may obtain an object from a pool (popping the pool's free list, or creating a
new object if the free list is empty), and later return an object to a pool.
By managing storage allocation in this way, the frequency of garbage
collections can be reduced.
All pools are emptied when a garbage collection occurs. Garbage collections
occur asynchronously in an implementation-dependent manner.
(MAKE-POOL identification generator) -> pool
Creates a pool. Generator should be a procedure, and will be called by
OBTAIN-FROM-POOL whenever the pool's free list is empty. Identification
is only for identification purposes, for example, when the pool is
printed.
(OBTAIN-FROM-POOL pool) -> object
Obtains an object from pool. If the pool is empty, the pool's generator
is called to obtain an object, which is then returned directly.
(RETURN-TO-POOL pool object) -> undefined
Returns object to pool. A future call to OBTAIN-FROM-POOL may yield
object.
13.8. Weak pointers
(OBJECT-HASH object) -> integer
Returns a unique numeric identifier for object. That is,
(EQ? object1 object2)
if and only if
(= (OBJECT-HASH object1) (OBJECT-HASH object2))
Because OBJECT-HASH is invertible (see below), it can be used to create
weak pointers to objects, that is, "pointers" or "references" which are
-75-
not strong enough to prevent an object from being reclaimed by the garbage
collector. This concept of weak pointer is implemented by the integers
returned by OBJECT-HASH, which can be dereferenced by calling
OBJECT-UNHASH.
OBJECT-HASH is used by standard methods for the PRINT operation when
printing objects which have no read syntax.
(OBJECT-UNHASH integer) -> object or false
Returns the object whose unique identifier is integer, or false if the
object is no longer accessible (e.g. due to garbage collection).
(OBJECT-UNHASH (OBJECT-HASH object)) => object
Weak-sets provide a way to keep track of a collection of objects. They are
sometimes known as weak sets because they behave much like sets, but an object
in a weak-set may go away if the only pointer to the object is via the
weak-set. The garbage collector will remove such objects from weak-sets.
(MAKE-WEAK_SET identification) -> weak-set
Creates a new weak-set.
(ADD-TO-WEAK-SET weak-set object) -> undefined
Adds object to weak-set.
(REMOVE-FROM-WEAK-SET weak-set object) -> undefined
Removes object from weak-set.
(WEAK-SET->LIST weak-set) -> list
Returns a list of all objects currently in weak-set. Note that as long
as this list is accessible, none of the objects will be implicitly removed
from the weak-set, because they will be accessible via this list.
(WALK-WEAK-SET weak-set procedure) -> undefined
Calls procedure on each member of weak-set.
(WALK-WEAK-SET weak-set procedure)
<=>
(WALK procedure (WEAK-SET->LIST weak-set))
[(WEAK-SET-MEMBER? object weak-set) -> boolean procedure
returns true if object is a member of weak-set, otherwise false.
--T>3 only.
(WEAK-SET-EMPTY? weak-set) -> boolean procedure
returns true if weak-set is empty, otherwise false.
--T>3 only.]
-76-
Chapter 14 Syntax
The T standard environment includes routines which perform syntactic and
semantic analysis of T programs. There are two such subsystems within T,
corresponding to the language's two syntactic levels (see chapter 2). These
are the reader and the compiler.
In an attempt to make each of these subsystems as generally useful and flexible
as possible, they are not restricted to processing the language as described in
this manual. Instead, they each operate with respect to parameter clusters
known as read tables, in the case of the reader, or syntax tables, in the case
of the compiler.
14.1. The reader X10
The reader is a procedure available in the standard environment as the value of
the variable READ-OBJECT. Conceptually, the reader coerces a port of
characters (external representation) to a port of objects (internal
representations) via a mechanism known as parsing.
(READ-OBJECT port read-table) -> object or end-of-file
READ-OBJECT employs the READ-CHAR (page 89) and UNREAD-CHAR (page 89)
operations in order to parse an object according to the port's read
table.
If the port is empty, the end-of-file token is returned.
READ-OBJECT is called by the default method for the READ operation (page
89), so the reader is usually invoked indirectly by calling READ, not by
calling READ-OBJECT directly. When invoked from READ, the second argument
to READ-OBJECT is obtained by calling the PORT-READ-TABLE operation on
the port.
The reader works as follows:
Any whitespace characters (space, tab, newline, carriage return, line feed, or
form feed) are read and ignored. A non-whitespace character is obtained; call
it c.
If c is a read-macro character, the reader invokes a specialist routine to
handle a syntactic construct introduced by the read-macro character.
If c is not a read-macro character, then characters are read and saved until a
delimiter character is read. A delimiter character is either a whitespace
character, or one of the following: ( (left parenthesis), ) (right
parenthesis), [, ], {, }, or ; (semicolon). If the sequence of characters
beginning with c and going up to but not including the delimiter is parsable as
a number, then the sequence is converted to a number, which is returned.
Otherwise the sequence is converted to a symbol.
The escape character, backslash (\), may be used within a run of constituent
characters to include unusual characters in a symbol's print name. In this
case, the escaped character (i.e. the character following the escape character)
is treated as if it were a constituent character, and is not converted to upper
case if it is a lower case letter. For example:
-77-
abc\;def reads the same as #[Symbol "ABC;DEF"]
\a\bcdef reads the same as #[Symbol "abCDEF"]
\12345 reads the same as #[Symbol "12345"]
\'12345 reads the same as #[Symbol "'12345"]
The following are standard read-macro characters:
" Doublequote: introduces a string. Characters are read until
another doublequote character is found which does not
immediately follow a backslash (\), and a string is returned.
Within a string, backslash acts as an escape character, so that
doublequotes and backslashes may appear in strings.
' Quote: 'object reads the same as (QUOTE object).
( Left parenthesis: begins a list.
) Right parenthesis: ends a list or vector, and is illegal in
other contexts.
` Quasiquote: see section 10.4.
, Comma: this is part of the quasiquote syntax.
; Semicolon: introduces a comment. Characters are read and
discarded until a newline is encountered, at which point the
parsing process starts over.
# Sharp-sign: another dispatch to a specialist routine is
performed according to the character following the #.
Standard sharp-sign forms:
#\ Character syntax. See section 12.
#x Hexadecimal input. An integer following the #x is read in base
16.
#o Octal input. An integer following the #o is read in base 8.
#b Binary input. An integer following the #b is read in base 2.
#(...) Vector. The elements of a vector are read between the
parentheses, and the vector is returned.
#[...] This syntax is used for certain kinds of re-readable objects.
It also provides an alternate syntax for characters and
symbols. The brackets enclose a sequence of objects; the first
should be a symbol which keys the type of the resulting object,
e.g. CHAR or SYMBOL. For example,
#[Ascii 65] represents the same object as #\A
#[Symbol "FOO"] represents the same object as FOO
-78-
This syntax is used by the printer when necessary, for example:
(STRING->SYMBOL "") => #[Symbol ""]
(ASCII->CHAR 128) => #[Ascii 128]
#{...} This is the syntax used by the printer for objects which have
no reader syntax. When the reader encounters the sequence #{
it signals an error.
14.2. Read tables and read macros X10
Read tables package a number of parameters for use by programs which parse,
generate, or otherwise manipulate external syntax of programs and objects. In
particular, every read table contains a table which maps characters to objects
which describe their lexical properties.
There is a standard read table which contains the standard read syntax for all
characters. In order to define nonstandard read syntax, one must create a new
read table using MAKE-READ-TABLE, and arrange for READ-OBJECT to use that read
table instead of the standard read table, e.g. by doing
(SET (PORT-READ-TABLE ...) ...).
(MAKE-READ-TABLE read-table identification) -> new-read-table
Creates a new read table which is a copy of read-table. Identification
serves for debugging purposes only.
(DEFINE *MY-READ-TABLE*
(MAKE-READ-TABLE STANDARD-READ-TABLE '*MY-READ-TABLE*))
STANDARD-READ-TABLE -> read-table
The standard T read table.
VANILLA-READ-TABLE -> read-table
The value of VANILLA-READ-TABLE is a read table in which all all graphic
characters have ordinary non-read-macro constituent syntax, whitespace
characters have whitespace syntax, and other characters (e.g. control
characters) are illegal.
(WITH-INPUT-FROM-STRING (PORT " foo () ")
(SET (PORT-READ-TABLE PORT) VANILLA-READ-TABLE)
(LIST (READ PORT) (READ PORT)))
=> (FOO #[Symbol "()"])
(READ-TABLE-ENTRY table character) -> syntax Settable
Access character's read syntax in table. The entry for a given character
is some object which represents the character's lexical properties, for
example, whether it is a constituent, whitespace, or read-macro character.
For read-macro characters, the entry is a procedure for parsing the
read-macro construct.
-79-
Values suitable to be stored in read tables may be obtained by accessing
existing entries in the standard read table. For example:
(SET (READ-TABLE-ENTRY *MY-READ-TABLE* #\;)
(READ-TABLE-ENTRY STANDARD-READ-TABLE #\:))
makes the read syntax of semicolon in *MY-READ-TABLE* be the same as the
standard read syntax of colon (which is a contituent character).
To define a read macro, do
(SET (READ-TABLE-ENTRY read-table character) procedure)
where procedure is a procedure taking two arguments: a port and a
character. The port is the port which is currently being parsed by
READ-OBJECT. It may be passed as the port argument to input routines
like READC and READ-REFUSING-EOF if the read-macro needs to parse further
characters from the input port. The character is the character which
caused the read macro to be invoked; that is, it is the character under
which the procedure is stored in the read table.
Example:
(SET (READ-TABLE-ENTRY *MY-READ-TABLE* #\')
(LAMBDA (PORT CH)
(IGNORE CH)
(LIST 'QUOTE (READ-REFUSING-EOF PORT))))
Note that the standard read table and the vanilla read table are
immutable, and so their entries may not be changed.
NOTHING-READ -> object
Read macros should return this object, which is treated specially by the
reader, if they want to return no object. For example, the semi-colon
(comment) read-macro might be defined as follows:
(SET (READ-TABLE-ENTRY *MY-READ-TABLE* #\;)
(LAMBDA (PORT CH)
(ITERATE LOOP ()
(LET ((C (READC PORT)))
(COND ((EOF? C) C)
((CHAR= C #\NEWLINE) *NOTHING-READ*)
(ELSE (LOOP)))))))
(DELIMITING-READ-MACRO? procedure) -> boolean Operation
If an object which returns true to the DELIMITING-READ-MACRO? operation is
stored in a read table under a given character, then the reader will treat
the character as a delimiter (non-constituent) character. By default,
read-macro procedures return false to this predicate. Thus to make a
read-macro character be a delimiting character also (as are parentheses
and semicolon in the standard read table), its read table entry should
handle this operation by returning true.
-80-
(SET (READ-TABLE-ENTRY *MY-READ-TABLE* #\~)
(OBJECT (LAMBDA (PORT CH)
...)
((DELIMITING-READ-MACRO? SELF) T)))
(MAKE-LIST-READER) -> list-reader
The two procedures MAKE-LIST-READER and LIST-TERMINATOR can be used
together to define read macros which behave syntactically like
parentheses.
Each call to MAKE-LIST-READER returns an object which is a procedure of
two arguments called a list reader. Calling LIST-TERMINATOR on a list
reader will return another object called its list terminator.
List readers and terminators are suitable for entry in read tables. A
list reader acts as a read macro which reads a sequence of objects,
terminated by a character whose read syntax is the corresponding list
terminator. For example, the standard syntax for left and right
parentheses might be defined as follows:
(LET ((LIST-READER (MAKE-LIST-READER)))
(SET (READ-TABLE-ENTRY STANDARD-READ-TABLE #\LEFT-PAREN)
LIST-READER)
(SET (READ-TABLE-ENTRY STANDARD-READ-TABLE #\RIGHT-PAREN)
(LIST-TERMINATOR LIST-READER)))
Like any read-macro procedure, a list reader is a procedure of two
arguments. The first argument must be a port, and the second is
ignored. Thus instead of being stored in a read table, it may be called
from another read-macro procedure. For example, the following makes [...]
an alternative read syntax for vectors:
(LET ((LIST-READER (MAKE-LIST-READER)))
(SET (READ-TABLE-ENTRY *MY-READ-TABLE* #\LEFT-BRACKET)
(OBJECT (LAMBDA (PORT CH)
(LIST->VECTOR (LIST-READER PORT CH)))
((DELIMITING-READ-MACRO? SELF) T)))
(SET (READ-TABLE-ENTRY *MY-READ-TABLE* #\RIGHT-BRACKET)
(LIST-TERMINATOR LIST-READER)))
List readers and terminators handle the DELIMITING-READ-MACRO? operation
by returning true.
(LIST-TERMINATOR list-reader) -> list-terminator
Given a list reader, returns its list terminator. See LIST-READER, above.
14.3. Standard compiler
A compiler is a procedure which accepts an expression and a syntax table, and
returns a compiled code object. A compiled code object may be executed in a
given environment. Note that the term "compiler" is used in a technical sense
-81-
and encompasses not only compilers such as TC (section 17.5) which produce
machine instructions, but also programs such as the standard compiler (which is
called by EVAL) which operate internally by producing intermediate code or by
interpreting source code directly. Often programs like these are called
interpreters instead of compilers.
A given T implementation may have several compilers. STANDARD-COMPILER should
be one of these compilers. Tau 2.7 also provides a compiler called TC, which
is described in section 17.5.
(EVAL expression environment) -> object
Evaluates expression in environment. Evaluation is performed as a
two-stage process: first, the standard compiler compiles the expression,
producing a compiled code object; then the compiled code object is invoked
in the given environment.
(EVAL expression environment)
<=>
(RUN-COMPILED-CODE (STANDARD-COMPILER expression
(ENV-SYNTAX-TABLE environmen
environment)
(STANDARD-COMPILER expression syntax-table) -> compiled-code
Compiles expression. An implementation of T may provide several
compilers, of which STANDARD-COMPILER will be the one which is invoked by
EVAL.
(RUN-COMPILED-CODE compiled-code environment) -> object
Invokes a compiled code object.
Bug: In Tau 2.7, the environment passed to RUN-COMPILED-CODE must be
the same as the one whose syntax table was passed to
STANDARD-COMPILER.
14.4. Syntax tables
A syntax table maps symbols to syntax descriptors. Every syntax descriptor is
itself either a macro expander, or a unique token identifying a primitive
special form type.
Every locale has an associated syntax table. A locale's syntax table contains
definitions of special forms which are local to the locale. Each such syntax
table inherits entries lexically from the syntax tables of enclosing locales.
Macros provide a mechanism for extending the syntax of T by means of
source-to-source transformations. As in many Lisp dialects, the macro facility
in T provides a powerful tool for amplifying the expressiveness of the
language. But like any powerful tool, macros may be abused. They may easily
lead to programs that are very hard to understand.
-82-
Macros are defined by entering syntax descriptor objects known as macro
expanders into syntax tables; see SYNTAX-TABLE-ENTRY and DEFINE-SYNTAX, below.
Macros may also be defined locally to a file or expression using
DEFINE-LOCAL-SYNTAX or LET-SYNTAX.
Procedure integration is preferable to the use of macros in situations where
either would be applicable. See DEFINE-INTEGRABLE, page 71.
(ENV-SYNTAX-TABLE environment) -> syntax-table
Returns the syntax table associated with environment.
(MAKE-SYNTAX-TABLE syntax-table identification) -> syntax-table
Creates a new syntax table inferior to the given syntax table. Note that
syntax tables are created implicitly by MAKE-LOCALE (page 16).
STANDARD-SYNTAX-TABLE -> syntax-table
A syntax table with entries for all standard T reserved words.
STANDARD-SYNTAX-TABLE <=> (ENV-SYNTAX-TABLE-ENTRY STANDARD-ENV)
(SYNTAX-TABLE-ENTRY syntax-table symbol) -> descriptor or false Settable
Accesses the syntax descriptor associated with symbol in syntax-table.
Returns false if there is no such entry.
(SYNTAX-TABLE-ENTRY STANDARD-SYNTAX-TABLE 'QUOTE) => #{Syntax
(SYNTAX-TABLE-ENTRY STANDARD-SYNTAX-TABLE 'CAR) => false
Syntax table entries may be created or altered using
(SET (SYNTAX-TABLE-ENTRY ...) ...) or by using DEFINE-SYNTAX. On
assignment, descriptor may be false, in which case symbol loses any syntax
table entry it may have had. This allows it to be bound as a variable
using DEFINE or LET, for example.
14.5. Defining syntax
(DEFINE-SYNTAX symbol descriptor) -> undefined
(DEFINE-SYNTAX (symbol . vars) . body) -> undefined
Sets symbol's syntax table entry in the syntax table of the environment in
which the DEFINE-SYNTAX form is being evaluated. The second form is an
abbreviation for an equivalent expression of the first form involving
MACRO-EXPANDER:
(DEFINE-SYNTAX (symbol . variables) . body)
<=>
(DEFINE-SYNTAX symbol
(MACRO-EXPANDER (symbol . variables) . body))
Macros and MACRO-EXPANDER are explained below.
-83-
As with (SET (SYNTAX-TABLE-ENTRY ...) ...), descriptor may be false, in
which case symbol loses any syntax table entry it may have had. This
allows it to be bound as a variable using DEFINE or LET, for example.
Note that DEFINE-SYNTAX forms have no effect at compile time. Using them
indiscriminately may lead to code which behaves differently depending on
what compiler is being used. For example, a use of the special form
defined by a DEFINE-SYNTAX form later on in the same file in which the
DEFINE-SYNTAX form occurs may be seen as a valid special form reference by
the standard compiler, but may be treated as a call by TC.
(DEFINE-SYNTAX (REPEAT N . CODE)
`(LET ((COUNT ,N)
(THUNK (LAMBDA () ,@CODE)))
(DO ((COUNT COUNT (- COUNT 1)))
((<= COUNT 0) NIL)
(THUNK))))
14.6. Local syntax
Reserved words may be defined at compile time using LET-SYNTAX and
DEFINE-LOCAL-SYNTAX. Syntax defined this way is called local syntax and is in
effect only at compile time, not at run time.
Local syntax is block structured, much as variables are. The outermost local
syntax contour is the point at which a compiler is invoked, which usually means
a file boundary. Inner contours are introduced by LET-SYNTAX forms.
Put another way, a local syntax table is created whenever a compiler is invoked
(LOAD, COMFILE, EVAL) and whenever a LET-SYNTAX form is compiled. Entries are
created in a local syntax table at compile time for syntax defined initially by
the LET-SYNTAX form and later when the compiler encounters DEFINE-LOCAL-SYNTAX
forms. The syntax table is used at compile time and is otherwise unavailable.
(LET-SYNTAX specs . body) -> value-of-body
Defines macros locally to body. Yields the value of body, an implicit
block. Each spec should be either
(symbol descriptor)
or
((symbol . vars) . body)
in analogy to DEFINE-SYNTAX.
The descriptor and body forms in specs will not necessarily run in an
environment which is at all related to the environment in which the
program in which the LET-SYNTAX form occurred will be run, because
compilation may occur independently of execution. TC executes these forms
in (TC-MACRO-DEFINITION-ENV) [T<3 only]. The standard compiler uses the X10
locale with which the syntax table passed to it is associated. This is
-84-
implementation-dependent, and subject to change. For this reason, it is
best to write local macros in such a way that no free variables or special
forms are used, other than those in the standard system environment, that
is, those defined to be part of the T language.
This disclaimer does not apply to the body of the LET-SYNTAX form, which
is evaluated (except for syntax) exactly as if the LET-SYNTAX expression
were a BLOCK expression.
(LET-SYNTAX ((KWOTE (SYNTAX-TABLE-ENTRY STANDARD-SYNTAX-TABLE
'QUOTE)))
(KWOTE (A B C)))
=> (A B C)
(LET-SYNTAX ((SET NIL)) (LET ((SET LIST) (X 5)) (SET X 8))) => (
(LET-SYNTAX (((MAC X) `'(X = ,X))) (MAC Y)) => (X = Y)
Bug: Tau 2.7 doesn't implement LET-SYNTAX.
(DEFINE-LOCAL-SYNTAX symbol descriptor) -> undefined Special form
(DEFINE-LOCAL-SYNTAX (symbol . vars) . body) -> undefined
Defines syntax locally to the body of the nearest enclosing LET-SYNTAX
form, or, if the DEFINE-LOCAL-SYNTAX does not appear inside a LET-SYNTAX
form, then to the file or outermost expression in which it occurs.
Forward references are not defined to work; the DEFINE-LOCAL-SYNTAX form
should appear prior to any use of symbol as a reserved word.
The syntax of DEFINE-LOCAL-SYNTAX is analogous to that of DEFINE-SYNTAX.
In general, DEFINE-LOCAL-SYNTAX should be used for syntax which is to be
available only within the file in which it occurs. If a syntax definition
is needed for several files, then they should be made available in some
locale's syntax table by evaluating DEFINE-SYNTAX forms in that locale,
and then that locale's syntax table should be used when compiling or
loading the file (see the SYNTAX-TABLE file header clause).
Note: To ease incremental debugging, the standard compiler in Tau 2.7
causes syntax defined with DEFINE-LOCAL-SYNTAX to be retained
indefinitely; that is, they are entered into the syntax table of the
locale which was passed to LOAD. Programs should not rely on this
feature, however, or code may behave differently when compiled using
TC.
14.7. Macro expanders
(MACRO-EXPANDER (identification . variables) . body) -> macro-expanderSpecial f
Yields a macro expander. A macro expander is a kind of syntax descriptor,
and may therefore be stored in a syntax table. When a compiler using a
symbol table S encounters a form whose car is a symbol, and the entry in S
for that symbol is the object yielded by a MACRO-EXPANDER-expression, then
the macro expander is invoked; that is, its variables are bound to the
rest of form (as with one level of DESTRUCTURE binding), the body (an
implicit block) is evaluated, and the value is returned to the compiler.
The compiler then compiles that form in place of the original one.
-85-
The lexical context of body is that of the MACRO-EXPANDER form (augmented
by the bindings of variables, of course), as with LAMBDA.
(DEFINE M (MACRO-EXPANDER (FOO X Y Z) `(LIST 'FIRST ',X ,Y ,Z)))
(INVOKE-MACRO-EXPANDER M '(BAR QUOTED (+ 1 2) (* 3 4)))
=> (LIST 'FIRST 'QUOTED (+ 1 2) (* 3 4))
(DEFINE L (MAKE-LOCALE STANDARD-ENV NIL))
(SET (SYNTAX-TABLE-ENTRY (ENV-SYNTAX-TABLE L) 'BAR) M)
(EVAL '(BAR QUOTED (+ 1 2) (* 3 4)) L) => (FIRST QUOTED 3 12)
(DEFINE-SYNTAX FOO
(MACRO-EXPANDER (FOO THING FORM)
`(LIST ,FORM ',THING)))
(FOO (CONS 1 2) (CONS 3 5)) => ((3 . 5) (CONS 1 2))
DESTRUCTURE (page 52) and quasiquote (page 53) are useful in writing macro
expansion procedures, the first for taking apart the form which is to be
expanded, the second for constructing the resultant code from templates.
Note that for a macro definition to take effect at compile time, it must
either be present in the syntax table being used by the compiler or defin-
ed locally using LET-SYNTAX or DEFINE-LOCAL-SYNTAX.
(MACRO-EXPANDER? descriptor) -> boolean
Returns true if descriptor, which must be a syntax descriptor, is a macro
expander.
(MACRO-EXPANDER? (MACRO-EXPANDER (FOO X) X)) => true
(INVOKE-MACRO-EXPANDER descriptor form) -> new-form
Invokes the macro expansion procedure for descriptor, which must be a
macro expander. (See MACRO-EXPANDER, above.)
(INVOKE-MACRO-EXPANDER (MACRO-EXPANDER (FOO X) `(LAMBDA () ,X))
'(BAZ (+ 1 2)))
=>
(LAMBDA () (+ 1 2))
(MACRO-EXPAND form syntax-table) -> new-form
Performs one macro expansion on the form, if it is a list whose car is a
symbol, there is an entry in the given syntax-table for that symbol, and
that entry is a macro expander.
-86-
Chapter 15 ports
A port is any object which handles port operations. In general, ports
are objects which contain pointers into sequences of objects. The port
operations provide for obtaining objects from such a sequence, advancing a
port's pointer, performing side effects on the sequence itself, and so forth.
T programs communicate with the external world via ports. ports may access
file systems or terminals. They may also be used to implement filters and
buffers of various sorts.
ports are manipulated using generic operations. Most of the procedures
described below are generic operations, and their descriptions either specify
the behavior of their default methods or of the methods for system-supplied
ports. Users may create ports to their own specifications using
OBJECT-expressions. As long as a user port supports READ-CHAR and
UNREAD-CHAR (for input ports) or WRITE-CHAR (for output ports), most of the
other I/O operations will work with them.
15.1. General
(PORT? object) -> boolean Type predicate operation
Returns true if object is a port.
(INPUT-PORT? object) -> boolean Type predicate operation
Returns true if object is an input port.
(OUTPUT-PORT? object) -> boolean Type predicate operation
Returns true if object is an output port.
(INTERACTIVE-PORT? object) -> boolean Type predicate operation
Returns true if object is an interactive port. An interactive port is
any input port which is likely to have a human being at the other end,
for example, a terminal input port.
(EOF? object) -> boolean Type predicate
Returns true if object is the end-of-file token. ("End-of-port" would
be a more appropriate term.)
EOF -> end-of-file
This global variable holds the end-of-file token.
-87-
(WITH-OPEN-STREAMS specs . body) -> object Special form
WITH-OPEN-STREAMS has the same syntax as LET, and similar semantics. Each
spec should be of the form (variable port). Each port expression is
evaluated, and should evaluate to a port; for example, port would
typically be an expression involving OPEN or MAYBE-OPEN. The body, an
implicit block, is evaluated in a lexical context in which each variable
is bound to the value of the corresponding port expression. The ports
are closed, and the value of body is returned.
WITH-OPEN-STREAMS is the preferred way to use port creation operations
such as OPEN. It ensures that the ports will be closed, even if there
is a non-local exit (a (RESET) or any other kind of throw) out of body or
any of the port expressions. (See UNWIND-PROTECT, page 29.)
For an example, see OPEN, page 98.
(CLOSE port) -> undefined Operation
Closes port; indicates that no more input or output is intended, and
that any resources associated with it may be freed.
(STRING->INPUT-PORT string) -> port
Returns an input port which yields successive characters of string on
successive READ-CHAR's.
(WITH-INPUT-FROM-STRING (variable string) . body) -> value-of-body Special form
Opens an input port to string, binds variable to that port, and
evaluates body, returning whatever body returns.
(WITH-INPUT-FROM-STRING (variable string) . body)
<=>
(WITH-OPEN-STREAMS ((variable (STRING->INPUT-PORT string)))
. body)
(WITH-OUTPUT-TO-STRING var . body) -> string Special form
Binds var to an output port. The body (an implicit block) is evaluated,
and any characters written to the port are collected in a string, which
is returned as the value of the WITH-OUTPUT-TO-STRING form.
(WITH-OUTPUT-TO-STRING FOO (WRITE FOO '(A B))) => "(A B)"
-88-
15.2. Port switches
(TERMINAL-INPUT) -> port Settable
Accesses the default port for input from the terminal.
Note that an end-of-file condition on the terminal input port will cause
the end-of-file token to be returned as the value of the next input oper-
ation on that port, but will not cause the port to become closed. This is
an exception to the normal rule that no input is available from ports fol-
lowing an end-of-file condition on it.
(TERMINAL-OUTPUT) -> port Settable
Accesses the default port for output to the terminal.
(STANDARD-INPUT) -> port Settable
Accesses the default standard input port. Initially, this is the same
as the value of (TERMINAL-INPUT).
The intent is that a program could do all its input from the standard
input port. By default, input would come from the terminal. But some
procedure could set the standard input port to be a file, for example,
and then call the program, which would automatically read from the file.
This same idea is used for all the following predefined ports.
(STANDARD-OUTPUT) -> port Settable
Accesses the default standard output port. Initially, this is the same
as the value of (TERMINAL-OUTPUT).
(ERROR-OUTPUT) -> port Settable
Initially, yields the same value as (TERMINAL-OUTPUT).
(DEBUG-OUTPUT) -> port Settable
Initially, yields the same value as (TERMINAL-OUTPUT).
15.3. Input
(READ-CHAR port) -> character or end-of-file Operation
(READC port) -> character or end-of-file
Reads a single character from port, advances the port pointer, and
returns the character read. If no character is available, then the
end-of-file token is returned.
-89-
(UNREAD-CHAR port) -> undefined Operation
(UNREADC port) -> undefined
Backs up port's pointer into its corresponding sequence of characters by
one character. This causes the next READ-CHAR from port to return
character instead of actually reading another character. Character is
returned. UNREAD-CHAR is convenient for implementing one-character-
lookahead scanners. Only the previous character READ-CHAR'ed from port
may be put back, and it may be put back only once.
(PEEK-CHAR port) -> character or end-of-file Operation
(PEEKC port) -> character or end-of-file
Returns the next character available on port without advancing the
port's pointer. If no character is there then the end-of-file token is
returned.
(PEEK-CHAR port) <=>
(BLOCK0 (READ-CHAR port) (UNREAD-CHAR port))
(READ-LINE port) -> string or end-of-file Operation
Reads a line of input from port and returns it as a string. If no input
is available then the end-of-file token is returned.
(READ port) -> object or end-of-file Operation
Reads an object from port. The default method simply calls READ-OBJECT,
passing it the port and the port's current associated read table. See
READ-OBJECT, page 77, and PORT-READ-TABLE, page 93.
(READ-REFUSING-EOF port) -> object
This is like READ but should be used in cases where an end-of-file is not
appropriate or is not handled, such as within the definition of a read
macro.
(READ-OBJECTS-FROM-STRING string) -> list
Returns a list of all the objects that could be read from the string.
(READ-OBJECTS-FROM-STRING "A B C") => (A B C)
(READ-OBJECTS-FROM-STRING " 015 ( Foo ) ") => (15 (FOO))
(READ-OBJECTS-FROM-STRING "") => ()
(CLEAR-INPUT port) -> undefined Operation
Discards any buffered input for port. The precise action of this
operation is implementation-dependent.
-90-
15.4. Output
(PRINT object port) -> undefined Operation
Prints object on port according to the current read-table. This is an
operation, so objects may decide how they would like to print.
(WRITE port object) -> undefined Operation
Prints the object on port. This is like PRINT with the argument order
reversed. This is an operation, so particular ports may decide how they
want to write objects to themselves.
(WRITE-CHAR port character) -> undefined Operation
(WRITEC port character) -> undefined
Writes a single character to port.
(WRITE-STRING port string) -> undefined Operation
(WRITES port string) -> undefined
Writes string to port.
(WRITE-LINE port string) -> undefined Operation
Writes string to port (like WRITE-STRING), then does a NEWLINE
operation.
(WRITE-SPACES port count) -> undefined
Writes count space characters to port.
(DISPLAY object port) -> undefined Operation
Prints object on port, but omits any slashes, delimiters, etc., when
printing strings, symbols, and characters.
(PRETTY-PRINT object port) -> undefined Operation
Pretty-prints object on port.
(NEWLINE port) -> undefined Operation
Begins a new line on the given output port.
(FRESH-LINE port) -> undefined Operation
If not at the beginning of a line, begins a new line on the given output
port.
-91-
(SPACE port) -> undefined Operation
Write whitespace to port. Ordinarily this will simply write a space
character, but if the current output line has overflowed a "reasonable"
right margin, this will do a NEWLINE.
(FORCE-OUTPUT port) -> undefined Operation
Makes sure any buffered output to port is forced out. This is useful
especially with terminal output ports. The precise behavior of this
operation is implementation-dependent.
15.5. Formatted output
(FORMAT destination control-string . rest) -> string or undefined
Performs formatted output. Characters in the control-string other than
tilde (~) are simply written to destination. When a tilde is encountered,
special action is taken according to the character following the tilde.
Destination should be one of the following:
- A port. In this case, output is simply written to the port.
The value returned by the call to FORMAT is undefined.
- The standard true value. (This is the value of the system
variable T.) This is equivalent to a port argument of
(STANDARD-OUTPUT). The value returned by the call to FORMAT is
undefined.
- Null. In this case the output is collected in a string, as with
WITH-OUTPUT-TO-STRING. This string is returned as the value of
the call to FORMAT.
The FORMAT control sequences are as follows. (Case is irrelevant, so ~A
and ~a behave identically.)
~A DISPLAY the next format argument.
~B Print the next argument in binary.
~D Print the next argument in decimal (radix ten).
~O Print the next argument in octal (radix eight).
~P Write the character "s" if the next argument,
which must be a number, is not equal to 1 (for plurals)
~R ~nR prints the next argument in radix n.
~S PRINT the next format argument.
~T ~nT tabs to column n (HPOS).
~X Print the next argument in hexadecimal (radix sixteen).
~% Go to a new output line (NEWLINE).
~& Go to a fresh output line (FRESH-LINE).
~_ Print a space, or go to a fresh output line (SPACE).
~~ Write a tilde.
-92-
A tilde followed by any whitespace character is ignored, along with all
following whitespace.
(FORMAT NIL "The ~s eats grass." 'ELAND) => "The ELAND eats gras
(FORMAT NIL "The ~A eats grass." "kudu") => "The kudu eats grass
(FORMAT NIL "~S had ~X goat~P." '(SANDY SMITH) 31 31)
=> "(SANDY SMITH) had 1F goats."
15.6. Miscellaneous
(PORT-READ-TABLE port) -> read-table Settable operation
Accesses the read table associated with port. See section 14.2.
(LINE-LENGTH port) -> integer Settable operation
Returns the maximum width that lines read from port are likely to take,
or that lines written to port ought to take.
Bug: In Tau 2.7, the LINE-LENGTH of system-supplied ports is not
settable.
(HPOS port) -> integer Settable operation
Accesses the current horizontal position (column number) of port. The
leftmost position on a line is column 0. When assigned, as many spaces as
necessary are written to bring the horizontal position to the assigned
value.
(VPOS port) -> integer Settable operation
Accesses the current vertical position (line number) of port. The
uppermost vertical position is line 0.
(WITH-OUTPUT-WIDTH-STREAM variable . body) -> integer Special form
Binds variable to an output port, and evaluates body in the augmented
lexical environment. The characters sent to the output port are not
accumulated, but merely counted, and the total is returned as the value of
WITH-OUTPUT-WIDTH-STREAM.
(PRINTWIDTH object) -> integer
Returns the number of WRITEC's which would be performed were object to be
printed using PRINT.
(PRINTWIDTH object)
<=>
(WITH-OUTPUT-WIDTH-STREAM PORT (PRINT object PORT))
-93-
(DISPLAYWIDTH object) -> integer
Returns the number of WRITEC's which would be performed were object to be
printed using DISPLAY.
(DISPLAYWIDTH object)
<=>
(WITH-OUTPUT-WIDTH-STREAM PORT (DISPLAY object PORT)
(MAKE-BROADCAST-PORT . output-ports) -> port
Returns a port which will "broadcast" all output operations to all of
the output-ports. For example, if the port s is the value of a call
(MAKE-BROADCAST-PORT q r)
then any WRITEC (WRITES, SPACE, etc.) operation to s will result in WRITEC
operations on both q and r.
15.7. Example
Here is an example of a user-defined port. MAKE-PREFIXED-PORT returns a
port which prefixes each line written to a given port with a given string.
(DEFINE (MAKE-PREFIXED-PORT PORT PREFIX)
(JOIN (OBJECT NIL
((NEWLINE SELF) (NEWLINE PORT) (WRITES PORT PREFIX)
((PRINT SELF OPORT)
(FORMAT OPORT
"#{Prefixed-port~_~S~_~S}"
PORT
PREFIX)))
PORT))
-94-
Chapter 16 Files
An executing T system interacts with the world outside by communicating with
various external entities, including:
- people, via keyboards and displays;
- file systems, via operating system calls, possibly over a
communications network;
- and processes, running on the same machine or on different machines.
The language provides simple standardized interfaces to the first two of these.
Particular implementations may provide more complete interfaces.
Standard access to a terminal is provided by the ports TERMINAL-INPUT and
TERMINAL-OUTPUT (page 90). Access to file systems is provided by the
facilities described in this chapter.
Disclaimer: The file naming facilities described in this chapter are quite
preliminary and are subject to change and elaboration. This documentation
corresponds to what is available in Tau 2.7.
16.1. File systems
A file system is a collection of named permanent objects called files, and a
mechanism for manipulating this collection. A file system object is a T object
which names or represents an actual file system. Where the distinction is
unimportant, a file system object is referred to simply as a file system.
A given T implementation may provide access to multiple file systems; such a
facility is not described in this manual. However, one may assume that at
least one file system, known as the local file system, is accessible.
(LOCAL-FS) -> file-system
Returns the local file system object.
The following type predicates are defined to query the type of a file system.
(AEGIS-FS? file-system) -> boolean Type predicate
Returns true if file-system represents an Aegis file system.
(UNIX-FS? file-system) -> boolean Type predicate
Returns true if file-system represents a Unix file system.
(VMS-FS? file-system) -> boolean Type predicate
Returns true if file-system represents a VMS file system.
-95-
16.2. Filenames
A filename is an object which names a file. Filenames are immutable record
structures with the following components:
- File system: the file system object via which the named file is
accessible.
- Directory: a collection of files within the file system to which the
named file belongs.
- Name: the name of the file within the directory, or of a family of
files, as further specified by the file type and generation
components of the filename.
- Type: the type of the file (also known as "extension").
- Generation: a positive integer specifying a particular version or
incarnation of the file. Larger generation numbers indicate newer
versions.
In general, the directory, name, and type components of a file are usually
symbols, the file system component is a file system object, and the generation
component is an integer. The file system component may be a symbol which names
a file system in an implementation-dependent manner.
Filenames may be incompletely specified; missing components are represented by
having null (false) as their value. An omitted file system is usually
interpreted to be the same as the local file system, an omitted directory
component means the current working directory. The name component may not be
omitted.
The external representation of a filename has the form
#[Filename file-system dir name type gen]
where gen and type may be omitted, if null.
(MAKE-FILENAME file-system dir name type gen) -> filename
(MAKE-FILENAME file-system dir name type) -> filename
(MAKE-FILENAME file-system dir name) -> filename
Returns a filename. The arguments become the components of the filename
object. Type and gen may be omitted, in which case they default to null
(absent).
(->FILENAME filespec) -> filename
Coerces filespec to a filename.
- If filespec is a filename, then it is returned.
- If filespec is a list l, then MAKE-FILENAME is called, passing
null as the file system argument, and the elements of the list
as the rest of the arguments.
- If filespec is a symbol x, then it is treated the same as the
list (() x).
- If filespec is a string, then it is converted to a filename in
an implementation-dependent way, such that FILENAME->STRING
applied to the filename will return a string which is equal to
filespec.
-96-
For example:
(->FILENAME '#[Filename () MATH FACT T]) => #[Filename () MATH F
(->FILENAME '(MATH FACT T)) => #[Filename () MATH F
(->FILENAME '(MATH FACT)) => #[Filename () MATH F
(->FILENAME 'FACT) => #[Filename () () FAC
(->FILENAME "fact.t") => #[Filename () () FAC
The last example is plausible, but it will not necessarily hold in all T
implementations, since the coercion of strings to filenames is not defined
here. (In Tau 2.7, strings are not parsed into separate filename
components.)
(FILENAME? object) -> boolean Type predicate
Returns true if object is a filename.
(FILENAME-FS filename) -> file-system or false
Returns the file system component of filename, or false (null) if it has
none.
(FILENAME-DIR filename) -> symbol or false
Returns the directory component of filename.
(FILENAME-NAME filename) -> symbol
Returns the name component of filename.
(FILENAME-NAME '#[Filename () MATH FACT T]) => FACT
(FILENAME-TYPE filename) -> symbol or false
Returns the type component of filename.
(FILENAME-GENERATION filename) -> integer or false
Returns the generation component of filename.
(FILENAME->STRING filename) -> string
Returns a string representing filename in the native syntax of filename's
file system (or of the local file system if filename is null).
In Tau 2.7, if the directory component of filename is a symbol, then it is
interpreted as a "logical name" in a manner idiosyncratic to the type of
the file system:
- Aegis: a T logical name is interpreted as being a link in the
naming directory.
- Unix: a T logical name is an environment variable.
- VMS: a T logical name is a VMS logical name.
-97-
For example:
(FILENAME->STRING '#[Filename AN-AEGIS-FS MATH FACT T])
=> "~math/fact.t"
(FILENAME->STRING '#[Filename A-UNIX-FS MATH FACT T])
=> "/usr/math/fact.t"
(FILENAME->STRING '#[Filename A-VMS-FS MATH FACT T])
=> "MATH:FACT.T"
(In the Unix example, we assume that environment variable MATH was defined
to be /usr/math, e.g. by a "setenv MATH /usr/math" shell command.)
16.3. Files
A file is an external permanent object stored in a file system. Files are
accessed in T via ports. Ordinarily, files are sequences of characters,
similar to strings. An input port open on an existing file delivers
successive characters (or lines, or parsed objects) out of the file. An output
port open on a new file deposits successive characters (or lines, or printed
representations of objects) into the file.
OPEN and MAYBE-OPEN obtain ports which access files. Any port created by
OPEN or MAYBE-OPEN should be closed (using the CLOSE operation, page 89) when
no further access to the file is required. This is guaranteed if OPEN and
MAYBE-OPEN are always used in conjunction with WITH-OPEN-STREAMS (page 90),
which ensures that any port opened actually gets closed, even if there is a
throw out of the body of the WITH-OPEN-STREAMS form.
(OPEN filespec mode-list) -> port
Opens an external file for reading or for writing, and returns a port
which accesses it. Filespec should be a filename or any other object
which can be coerced to one (see ->FILENAME, page 96). Mode-list should
be a list of keywords (symbols) specifying what kind of access to the file
is desired. The only keywords currently recognized are IN, OUT, and
APPEND, indicating reading, writing, and appending, respectively. It is
an error condition if the file cannot be opened for some reason.
(WITH-OPEN-STREAMS ((PORT (OPEN "file.txt" '(IN))))
(READ-LINE PORT))
(MAYBE-OPEN filespec modes) -> port or false
Like OPEN, but returns false if for any reason it cannot open the file.
(PORT-NAME port) -> filename Operation
Returns the filename of the file on which port is open. This operation
is not handled by any system-created ports other than those created by
OPEN and MAYBE-OPEN.
(FILE-EXISTS? filespec) -> boolean
Returns true if the specified file exists.
-98-
(FILE-MOVE source-filespec dest-filespec) -> undefined
Moves the file named by source-filespec to a new location given by
dest-filespec. In some cases, this operation can be performed without
actually copying the file, for example, if the two locations are on the
same "volume" of the same file system. In this case, FILE-MOVE is simply
a rename operation. In other cases, it may be more expensive.
Bug: Not all versions of Tau 2.7 implement FILE-MOVE.
(FILE-DELETE filespec) -> undefined
Deletes the specified file.
Bug: Not all versions of Tau 2.7 implement FILE-DELETE.
-99-
Chapter 17 Program structure
This chapter provides information about organizing, loading, and compiling
programs.
17.1. Environment structure
Lexical environments in T are hierarchically arranged. Variable bindings are
inherited from outer (superior) contours to inner (inferior) ones. At the top
of the hierarchy is a root environment, which has no bindings in it. Inferior
to that is a standard environment which has bindings for all standard system
variables, for example, CAR and +. (See section 2.3.) Inferior to the
standard environment are environments into which programs have been or are to
be loaded. In the standard environment, the variable STANDARD-ENV is bound
to the standard environment itself.
When a T system starts up, it sets up an initial environment configuration
which has one environment inferior to the standard environment, called the
user environment. The variable USER-ENV is bound in the standard
environment to the user environment. The user environment has no
variable bindings in it at first; however, the initial read-eval-print loop
(section 18.2) is started in this environment, so that if no other provision is
made, user global variable (i.e. definitions) will be made in the user
environment.
In Tau 2.7, there is another environment called the implementation environment
This is the value of T-IMPLEMENTATION-ENV in the standard environment. The imp-
lementation environment is not inferior to the standard environment, but in-
stead is inferior to the root.
------ root environment (empty) ----------
/ | \
------- STANDARD-ENV ------ T-IMPLEMENTATION-ENV
/ | | ... \
USER-ENV TC-ENV EWE-ENV other environments
/ ... \
[This system has changed in T>3] ... ... X18
Empty environments may be created using MAKE-EMPTY-LOCALE (page 16). For
example:
(DEFINE *ALMOST-USELESS-ENV* (MAKE-EMPTY-LOCALE '*ALMOST-USELESS-ENV*))
(*DEFINE *ALMOST-USELESS-ENV* '+ +)
(*DEFINE *ALMOST-USELESS-ENV* '- -)
(EVAL '(+ 5 (- 21 13)) *ALMOST-USELESS-ENV*) => 13
STANDARD-ENV -> locale
The value of STANDARD-ENV is an environment (a locale) in which all
system variables have appropriate values, as described in this manual.
That is, it is a standard environment in the sense of section 2.3.
-100-
USER-ENV -> locale
The value of USER-ENV is an environment (a locale) inferior to
STANDARD-ENV. It is provided by a T implementation as an environment in
which a user may evaluate expressions and write programs. Other
evaluation environments may be created inferior to STANDARD-ENV,
however, with MAKE-LOCALE (page 16).
17.2. Source files
T programs are usually represented by collections of one or more text files
resident in a file system. Text files containing T programs are called source
files.
A source file consists of a sequence of (external representations of) T
expressions. Source files may be loaded into a T environment. When a file is
loaded, the expressions in the file are evaluated. Typically, this means that
useful side-effects, such as procedure definitions, occur which then make the
program available in that T environment.
A compilation (semantic analysis) step must occur either as a file is being
loaded, or prior to loading the file. In the former case, compilation is
typically performed by a compiler such as the "standard compiler" (page 80ff)
which itself runs relatively quickly and produces intermediate code which must
then be interpreted. In the latter case, an auxiliary file known as an object
file is involved; the compilation step need only be performed once, even if the
file is to be loaded many times. (The term object file is completely unrelated
to the term object.)
A file compiler (such as TC; see section 17.5) takes a source file as input and
produces an object file as output. The object file may then be loaded in lieu
of the source file, with the same effect.
Note: In the current implementation, the standard compiler cannot be used
as a file compiler, and TC cannot be used as an "on-the-fly" compiler. In
principle, the two dimensions of compiler and compilation mode are
orthogonal: it should be possible to use either compiler in either
manner. In practice, this is not the case, but it turns out not to be too
much of a problem.
17.3. File syntax
The first form in a source file must be a list whose car is the symbol HERALD.
This form is not an expression, but rather is part of the syntax of the file.
It provides information relevant to programs which operate on the source file
(such as readers, compilers, and loaders). The syntax of a HERALD-form is as
follows:
(HERALD identification . items)
Identification should be either () or a symbol identifying the file (usually
the same as the root of the name of the file). It is for documentary purposes
only.
Items is a sequence of lists. Each item should be a list beginning with a
valid keyword symbol, as described below.
-101-
Example:
(HERALD FACT
(READ-TABLE *MATH-READ-TABLE*)
(ENV T (MATH MATHMACROS)))
(READ-TABLE expression) Herald item
Expression should evaluate to a read table, which is used in reading the
expressions which follow the HERALD-form from the source file. The
environment in which expression is evaluated depends on the program
processing the file (but might be, e.g., the user environment).
If this item is absent, then expressions are read using the standard read
table.
(SYNTAX-TABLE expression) Herald item
Expression should evaluate to a syntax table, which is used in compiling
(evaluating) the expressions in the source file. The environment in which
expression is evaluated depends on the program processing the file (but
might be, e.g., the user environment).
If this item is absent, then expressions are compiled according to an
appropriate syntax table: the syntax table associated with the locale into
which the file is being loaded, if the file is being loaded, or the
current value of (TC-SYNTAX-TABLE) (see below), if the file is being
compiled using TC.
Bug: In Tau 2.7, this works in TC, but is ignored in the standard
compiler (that is, when loading a source file directly). LOAD will
always use the syntax table of the environment into which the file is
being loaded.
(ENV support-env-name . filespecs) Herald item
Specifies a support (early binding) environment for compiling the file.
The support environment consists of the support environment named by
support-env-name, augmented by information obtained from support files
named by filespecs.
If this item is absent, then the standard support environment, whose name
is T, is used. The ability to create and name other support environments
is not yet documented.
17.4. Loading files
(LOAD filespec environment) -> undefined
Loads the file named by filespec. If the file is a source file, then each
expression in the file is compiled (with the standard compiler) and run in
environment. If the file is an object file, as produced by TC, then the
compiled object code is simply run in environment.
If no explicit file type is given in the filespec, then LOAD will load
either an object file (file type BIN), if one exists, or a source file
(file type T) otherwise.
-102-
17.5. File compilation
In addition to the standard compiler invoked when EVAL or LOAD is called, Tau
provides a so-called optimizing compiler. This compiler, known as TC (for "T
compiler"), trades compilation speed for execution speed: STANDARD-COMPILER
tries to compile quickly, while TC tries to generate executable code which will
run fast.
To help produce more efficient code, TC makes assumptions about the values that
some variables will have at run-time. These assumptions are called early
bindings. For example, it will ordinarily assume that the variable PAIR? will
have the standard PAIR? predicate as its top-level value. This means that if
an object file produced under this assumption is loaded into a lexical
environment where this is not the case, then calls to PAIR? will not execute
the same as they would if the source file had been loaded.
Early bindings are obtained from support environments. The support environment
to be used in compiling a file may be specified by an ENV clause in the file's
header. A support environment may contain user-defined integrable procedure and
constant definitions.
Besides early binding, another source of improved efficiency is a difference in
the handling of undefined effects (that is, run-time program errors). When
code compiled using the standard compiler incurs an error, an error is
signalled, and the error system is entered, giving the user an opportunity to
debug the problem at the point where it occurs. Code compiled using TC has
less error-checking, so the effect of an error may go unnoticed until long
after the error occurred, or a secondary error of an obscure or unexpected kind
will occur.
Instead of being a part of the normal Tau run-time system, the optimizing
compiler is invoked as a separate program with an appropriate system command
(probably "tc"). When TC starts up it is in a read-eval-print loop and looks
very much like Tau. In fact, TC is simply a version of Tau with the following
additional definitions available in STANDARD-ENV.
(COMFILE filespec) -> undefined
Compiles a file. For example, to compile file fact.t, say
(COMFILE "fact"). TC writes three output files, all having the same file
name as the T source, and distinguished by extension:
- The noise file is a transcript of what TC wrote to the terminal
in the course of the compilation. TC also writes some
additional statistics and cross-referencing information to noise
files.
- The support file contains early binding information useful for
compiling other files with TC. See the ENV file header clause,
page 102.
- The assembly file should be assembled on the target machine to
get an object file that can be loaded into Tau.
The file extensions for the output files depend on the target system. For
MC68000/Aegis, VAX/Unix, and VAX/VMS, the extensions are as follows:
System Architecture Support Noise Assembly
Aegis 68000 .ams .amn .asm
Unix VAX11 .uvs .uvn .s
VMS VAX11 .vvs .vvn .mar
-103-
(TC-SYNTAX-TABLE) -> syntax-table Settable
This is the basic syntax table from which TC obtains macro definitions.
TC obtains additional macro definitions from the support environment set
up by an (ENV ...) HERALD clause. Its default value is the syntax table
of the user environment.
(TC-MACRO-DEFINITION-ENV) -> environment Settable X10
The environment in which macro definition bodies themselves are evaluated,
either as a result of encountering a DEFINE-LOCAL-SYNTAX or LET-SYNTAX
expression, or by loading macro definitions from a support file. Its
default value is the user environment. [Removed in T>3.]
-104-
Chapter 18 User interface
This chapter describes the user interface to the Tau system, which is the
current implementation of the T language. Also part of the user interface are
the various debugging facilities, which are described in chapter 19.
The features described in this chapter are highly volatile and
implementation-dependent.
18.1. Invoking Tau
To invoke a Tau system, one typically gives a command to the system command
processor, as appropriate to the operating system and installation. For
example, under Aegis, the following interaction might take place:
$ t
This is MC68000/Aegis Tau version 2.8 (329)
;Loading "~sys/tfix/tfix329.t" into T-IMPLEMENTATION-ENV
;Loading "~init.t" into USER-ENV
> (list 'planner 'conniver)
(PLANNER CONNIVER)
>
The interaction may look slightly different in the other implementations. The
above illustrates the startup sequence, which proceeds as follows:
- The user invokes Tau with an appropriate command.
- The Tau system identifies itself by giving the processor and
operating system under which it believes itself to be running, the
major and minor system version numbers, and the system edit number.
The version numbers identify which release of Tau is running. The
edit number is useful to the system implementors and should be
provided in any bug reports sent to them.
- Tau loads a patch file, if one exists. The patch file consists of
forms which fix bugs in the current release of the system.
- Tau loads a user initialization file, if one exists. The user
initialization file consists of any forms which the users wants to
have evaluated when Tau starts up. The location in the file system
where the initialization file is found is system-dependent, but is
usually the file init.t in the user's home directory.
- Tau enters a read-eval-print loop. Read-eval-print loops are
described in section 18.2.
(STOP) -> undefined
Exits Tau in such a way that it may be resumed later. Control returns to
the context from which Tau was invoked. Under Unix, this usually means
the shell. Under VMS, this means the command interpreter (DCL). STOP is
not defined in Aegis Tau because the Aegis environment makes it
unnecessary.
-105-
(EXIT)
Exits Tau, returning control to the context in which Tau was invoked in
the first place (usually a shell or other command processor). Any
resources associated with the Tau process will be freed; EXIT is not
reversible. A call to EXIT cannot return.
(COMMAND-LINE) -> list
This function returns the command line used to invoke Tau, represented as
a list of strings.
18.2. Read-eval-print loops
The user usually interacts with the Tau system via a read-eval-print loop. As
illustrated above, this is a command loop which repeatedly reads an expression,
evaluates it, and prints the value.
The read-eval-print loop is a simple command processor; it prints a prompt,
reads a command from the terminal, executes the command, then prints another
prompt, ad infinitum. A command is any executable T form, and executing the
command consists of evaluating the form and printing the result.
## -> object
The variable ** always has as its value the last object which was printed
by the read-eval-print loop, that is, the value of the last expression
typed by the user.
++ -> object
The variable ++ always has as its value the last expression read by the
read-eval-print loop. It is not assigned this value, however, until after
the expression has been evaluated, so that a new expression may refer to
the previous one.
Expressions are read from the terminal by applying the READ operation
(actually, the value of (REPL-READ); see page 108) to the terminal input port.
Initially, that port's read table is the standard read table, but this may be
changed using SET, for example:
(SET (PORT-READ-TABLE (TERMINAL-INPUT)) *MY-READ-TABLE*)
Evaluation is performed with respect to a particular variable environment and
its syntax table. The read-eval-print loop may move from place to place within
the environment hierarchy; one may control the evaluation environment and
syntax table by setting (REPL-ENV).
(REPL-ENV) -> environment Settable
Accesses the environment passed to (REPL-EVAL) by the read-eval-print
loop. Initially, this is the user environment (see page 102).
-106-
18.3. Command levels
Read-eval-print loops may be invoked recursively. Each currently running
read-eval-print loop is said to be at a different command level. The initial
read-eval-print loop is at top level, and recursive read-eval-print loops are
at successively deeper levels.
The level of the current command loop is reflected in the way the loop prompts
for input. At top level, the prompt is a single greater-than sign (>). At
deeper command levels, the prompt contains as many greater-than signs as there
are active read-eval-print loops; for example, >>> if there are two recursive
read-eval-print loops beneath the top level one.
Deeper command levels are usually entered as the result of program execution
errors, but may also be entered because of a keyboard interrupt or an explicit
call to BREAKPOINT.
Interrupts: Issuing a keyboard interrupt will asynchronously enter a
read-eval-print loop. One may proceed from this breakpoint, at the point where
computation was interrupted, by doing (RET). Keyboard interrupts are normally
issued under Aegis by giving a Display Manager DQ command (normally assigned to
control-Q), under VMS by typing control-C, or under Unix by typing the
interrupt character (normally control-C or DEL).
End-of-file: An end-of-file condition on the terminal input port which occurs
at a read-eval-print loop will cause control to transfer up one command level
to the next read-eval-print loop. End-of-file can usually be
generated under Aegis with the display manager EEF command (normally assigned
to control-Z), under VMS by typing control-Z, or under Unix by typing the end
of file character (normally control-D).
(RESET)
Transfers control directly to the top-level read-eval-print loop by
performing a throw.
(RET) X5
(RET object)
Returns object (which defaults to an undefined value if not supplied) as
the value of the current read-eval-print loop. See section 19.1 for
examples.
(BREAKPOINT . message) -> object or undefined
Enters a read-eval-print loop. If message is not null, then it is printed
by DISPLAY on the (ERROR-OUTPUT) port. If (RET object) is called, then
the loop terminates and object is returned as the value of the call to
BREAKPOINT. If an end-of-file condition occurs, then control is thrown to
the read-eval-print loop at the next higher level, which continues.
18.4. Transcripts
A transcript is a record of the user's interaction with the T system.
Transcripts are necessary in T implementations under operating systems which do
not natively provide transcript facilities. Transcripts are not necessary, for
example, when running under the Aegis Display Manager.
-107-
(TRANSCRIPT-ON filename) -> undefined
Starts writing a transcript to the specified file. All output to
(TERMINAL-OUTPUT) and input from (TERMINAL-INPUT) between calls to
TRANSCRIPT-ON and TRANSCRIPT-OFF is written to the file. A subsequent
call to TRANSCRIPT-OFF will terminate the transcript, closing the file.
(TRANSCRIPT-OFF) -> undefined
Closes the active transcript file.
18.5. Customization
The various phases of the read-eval-print loop may be customized by assigning
values to switches.
(REPL-READ) -> procedure Settable
Accesses the READ part of the read-eval-print loop. Initially, this is
READ.
(REPL-EVAL) -> procedure Settable
Accesses the EVAL part of the read-eval-print loop. Initially, this is
EVAL.
(REPL-PRINT) -> procedure Settable
Accesses the PRINT part of the read-eval-print loop. Initially, this is
PRINT.
(REPL-PROMPT) -> procedure Settable
Accesses the prompting routine used in the read-eval-print loop. This
should be a procedure of one argument, which is a nonnegative integer
giving the command level (zero at top level, one at the next deeper level,
and so on). The procedure should return a string. Initially,
(REPL-PROMPT) is a procedure that returns "> " at the top level, ">> " at
the level below that, etc.
(LOAD-NOISILY?) -> boolean Settable
If (LOAD-NOISILY?) is true (which it is initially), then LOAD and REQUIRE
will print the value of each top-level expression in the file (standard
compiler only). The output goes to the terminal output port.
(REPL-WONT-PRINT? object) -> boolean Operation
Read-eval-print loops will not print any object which answers true to this
operation. Note that this is unrelated to the functioning of PRINT
itself.
-108-
REPL-WONT-PRINT -> object
This has as value an object which answers true to the REPL-WONT-PRINT?
predicate.
REPL-WONT-PRINT <=> (OBJECT NIL ((REPL-WONT-PRINT? SELF) T))
Chapter 19 Debugging
The facilities described in this chapter are highly implementation-specific,
and subject to change without notice. Facilities described here are intended
either for use directly as commands, or as utilities for user-written debugging
subsystems.
19.1. Errors
When the implementation detects an error condition, a message is printed and a
read-eval-print loop is entered in the dynamic context in which the error
occurred. A number of facilities are available at this point for debugging and
for recovering from the error.
Errors are usually detected in conditions which are left undefined by the
manual (see sections 2.4 and 13.2). These conditions include the following:
- An unbound variable is referenced.
- A procedure is called with too few or too many arguments.
- A non-procedure is called.
- An object of the wrong type is passed to a system procedure.
- A string or vector index is out of range.
- A generic operation is invoked on an object with no method to handle
it.
- The external representation of an object being parsed by READ-OBJECT
or a read macro is syntactically incorrect. (This kind of error is
called a read error.)
- An object being interpreted as an expression by a compiler or a macro
expander is syntactically incorrect. (This kind of error is called a
syntax error.)
- A call to ERROR or UNDEFINED-EFFECT occurs.
Once inside the read-eval-print loop, one may examine the current environment
(e.g., examining variables or the stack) using the read-eval-print loop
(section 18.2) or the inspector (section 19.3).
-109-
Typical actions after an error occurs include:
- The user corrects the error by returning a new value with RET.
- The user goes up one command level by typing the end-of-file
character (see page 108).
- The user throws to top level by calling (RESET) (page 108).
Example:
> (CADR 3)
** Error: attempting to take CADR of 3
>> (RET '(A B))
B
> (PLUS 3 4)
** Error: variable PLUS has no value
>> (RET ADD)
7
(RECKLESSNESS) -> LOW, MEDIUM, or HIGH Settable
Doing (SET (RECKLESSNESS) 'HIGH) will improve performance at the expense
of error-checking. Specifically, somewhere between 10 and 25 instructions
will be elided from most procedure calls, but wrong-number-of-argument,
undefined-procedure, and object-not-applicable errors will probably cause
illegal memory references or odd address errors instead of more
comprehensible error reports.
19.2. Debugging utilities
(TRACE variable) -> undefined Special form
Reassigns variable, whose value should be a procedure, to be a new
procedure which prints information on the (DEBUG-OUTPUT) port whenever
it is called or returns a value.
(UNTRACE variable) -> undefined Special form
If variable has a traced procedure as its value, UNTRACE restores it to
its original value. Otherwise it prints a warning and does nothing.
(PP procedure) -> undefined Special form
Prints the definition of procedure on the terminal output port. If the
source code is not available, then it prints the name of a file where it
can be found. (See also PRETTY-PRINT, page 91, and WHERE-DEFINED, page
113.)
(BACKTRACE) -> undefined
Prints a one-line summary describing each continuation on the stack. See
also DEBUG, page 111.
-110-
19.3. The inspector
CRAWL, also known as the inspector, is a stack and structure inspector. It
consists of a command loop and a set of commands. The inspector keeps track of
a current object, and a stack of objects which have previously been current
objects. Some inspector commands move from one object to another.
The command loop operates as follows: the current object is "summarized" (that
is, printed or displayed somehow); a prompt is printed; a line is read; and any
commands on the line are executed. One can give one or more commands on a
single input line.
The current object may be any object. If it is a continuation (stack frame),
the name of the procedure which contains the continuation's return point is
printed, and the prompt is "debug:". Otherwise, the object is printed (up to a
maximum of one line of output), and the prompt is "crawl:".
The meanings of some commands vary depending on what kind of object the current
object is, and not all commands are appropriate for all kinds of objects.
(DEBUG) -> undefined
(DEBUG) enters the inspector. The current object becomes a continuation
near the top of the stack (more precisely, the continuation to which the
value passed to RET will be given).
(CRAWL object) -> undefined
Enters the inspector. The current object becomes object.
Inspector commands:
? Help: prints a list of inspector commands, with one-line
summaries.
Q Quit: exits out of the inspector, usually back to the
read-eval-print loop.
U Up: pops the stack of saved objects. The current object is
forgotten, and the inspector moves to the previous current
object.
D Down: if the current object is a continuation, moves to the
next continuation deeper in the stack, that is, to the
continuation that was pushed prior to the current continuation.
X Exhibit: prints useful information about the current object.
The exact behavior of this varies depending on the type of the
object. For structures, all of the structure's components are
displayed. For continuations, any saved values are printed.
Integers are printed in various radices.
For some kinds of object, such as structures and vectors, the
contents of an object will be displayed in a menu form, as a
sequence of lines of the form "[key] component". In this case,
giving key as an inspector command will move to component,
which then becomes the current object.
-111-
integer Select element: in the case of a list or vector, this moves to
the appropriate component. For example, if the current object
is a list, then 3 as an inspector command is the same as A
CADDDR: it moves to the list's fourth element.
selector Select component: if the current object is a structure, typing
a selector name will move to that component of the structure.
B Breakpoint: enters a read-eval-print loop. Executing (RET)
will return back into the inspector. The read-eval-print loop
will execute either in (REPL-ENV), or, if the current object is
a continuation or procedure created by interpreted code (i.e.
code compiled by the standard compiler, as opposed to TC), in
the lexical environment of the object. Inside the breakpoint
loop, the system variable *OBJ* is bound to the current object.
C Crawl: moves to a new object. The object is obtained by
evaluating an input expression, which is read either from the
command line or in response to a prompt. The environment of
the evaluation is one appropriate to the current object, as
with the B command.
E Evaluate: evaluates an expression. The expression is read and
evaluated as with the C command. The result of the evaluation
is printed, but the current object remains the same.
A Apply: applies a procedure to the current object, and move to
the result of that call. An expression evaluating to the
procedure is read as with the C command.
M Macroexpand: performs one macro expansion on the current
object, which should be a list, and the current object becomes
the result of the macro expansion.
P Pretty-print: prints the current object using the pretty
printer. If the current object is a continuation, then this
command tries to print the expression to which the value
returned by the continuation is to be supplied.
R Return: if the current object is a continuation, returns a
value to it. The value is obtained as with the C, E, and A
commands.
W Where-defined: prints the result of calling WHERE-DEFINED on
the current object.
V Unit: moves to a template's or procedure's unit. Units are not
documented, but the X command works with them. This command is
intended primarily for the use of the Tau implementors.
The control stack which one inspects by invoking the inspector with (DEBUG) is
a sequence of continuations, or stack frames. These differ from activation
records in implementations of languages such as C or Lisp in that they
represent future computations rather than past calls. A full call history is
not easily available in tail-recursive languages such a T and Scheme. This is
implementation-dependent, of course, and future T implementations may maintain
call histories for debugging purposes.
-112-
Each continuation represents a control point which will make use of the value
returned to the continuation. Usually these control points correspond to the
arguments in a call, the predicate in an IF or COND, or any but the last
subform of a block. For example, when evaluating a call, a continuation is
constructed for each argument in the call, because these values will be used
when the call actually occurs. In the example below, the continuations into
FACT were mostly delivering values to the second argument position in the call
to *.
The three-column synopsis that the inspector prints for continuations is the
same as that printed by BACKTRACE (page 110). The first column is the name of
the procedure into which control will return, or (anonymous) if no procedure
name is available (as for procedures created by anonymous top-level
LAMBDA-expressions instead of by DEFINE). The second column is the name of the
source file containing the procedure, and the third column is relevant source
code, if available.
Here is a sample interaction with the inspector. Commentary is in Roman font
on the right. The terms "frame," "stack frame," and "continuation" are used
interchangeably.
> (define (fact n) Define factorial function.
(cond ((= n 0) i) Bug: i instead of 1.
(else (* n (fact (- n 1))))))
#{Procedure 120 FACT}
> (fact 4)
** Error: variable I is unbound Error detected by interpreter.
>> (debug)
#{Continuation 121} Current object is this frame.
BIND-INTERNAL THROW Internal to the implementation.
debug: d Go down one frame.
#{Dynamic-state-transition 122} Also internal, keep going.
debug: d
#{Continuation 123}
FACT () I Okay, this looks good.
debug: d
#{Continuation 124}
FACT () (* N (FACT (- N 1)))
debug: e n Evaluate N in this frame.
1 Value is 1.
debug: d
#{Continuation 125}
FACT () (* N (FACT (- N 1)))
debug: e n Value is 2 in this frame.
2
debug: d d Go down two frames.
#{Continuation 126}
FACT () (* N (FACT (- N 1)))
debug: d
#{Continuation 127}
READ-EVAL-PRINT-LOOP REPL FACT's caller (i.e. top level).
debug: u u u u u Up five frames.
#{Continuation 123}
FACT () I
debug: r 1 Return the value 1 to this fram
24 Execution proceeds, and the val
> 24 comes out.
-113-
19.4. Debugging primitives
This section describes routines which may be useful in writing debugging aids.
Note that they are not part of the language, and therefore should be avoided in
"ordinary" programs. Relying on these routines may lead to programs which
behave differently depending on how the programs are compiled (TC or standard
compiler), or which fail to work across releases.
(WHERE-DEFINED object) -> filename Operation
Tries to find a filename for the source file wherein object (usually a
procedure) is defined.
(IDENTIFICATION object) -> symbol or false Operation
If appropriate, this returns a symbol naming a variable which, in some
environment, might be defined to be the object. If no such identification
is appropriate, IDENTIFICATION returns false. This behavior is heuristic,
not contractual; for no value is IDENTIFICATION required to return
non-null.
(IDENTIFICATION CADR) => CADR
(LET ((X CADR)) (IDENTIFICATION X)) => CADR
(ARGSPECTRUM procedure) -> pair Operation
Returns an argspectrum for procedure. An argspectrum is a pair
(min . max) which describes the number of arguments that procedure expects
to receive when called. Min is always an integer giving the minimum
number of arguments expected; max is either an integer giving a maximum,
or it is (), meaning that exactly min arguments are required, or it is T,
meaning that any number of arguments (but at least min) are acceptable.
(DISCLOSE procedure) -> list or false
Attempts to reconstruct a source expression from which procedure may have
been compiled. This may work for code compiled using the standard
compiler, but is likely to fail to work for code compiled using TC.
Returns false if no source code can be obtained.
(GET-ENVIRONMENT procedure) -> environment or false
Attempts to reconstruct an environment in which procedure may have been
loaded (or run). This may work for code compiled using the standard
compiler, but is likely to fail to work for code compiled using TC.
Returns false if no environment can be obtained.
(STRUCTURE-TYPE object) -> stype or false
If object is a structure, this returns the structure type of which it is
an instance. If object is not a structure, it returns false.
STRUCTURE-TYPE is to be used with care since it may violate the data
protection otherwise provided by structure types. That is, anyone who is
given a structure may find out about its internals.
-114-
(WALK-SYMBOLS procedure) -> undefined
Calls procedure on every accessible symbol.
19.5. Miscellaneous
[(ENFORCE predicate value) -> value
If (predicate value) returns false, ENFORCE enters a breakpoint loop which
can then be used to return another value via (RET newvalue). [T>3]]
T-VERSION-NUMBER -> integer
An integer which gives the version number of the currently running T
implementation. The integer has the form
(+ (* major-version-number 1000) minor-version-number)
For example, in Tau 2.7, T-VERSION-NUMBER is 2007. Knowledge of the
version number may be useful in dealing with incompatibilities between Tau
releases, so that programs may conditionally adjust their state according
to the version, and thus be able to run in both older and newer releases.
T-IMPLEMENTATION-ENV -> locale
This environment contains variables internal to the implementation of T.
(IMPORT T-IMPLEMENTATION-ENV %%PAIR-TAG)
TC-ENV -> locale
This environment contains variables internal to the implementation of TC.
(GC) -> undefined
Invokes the garbage collector. Garbage collection is a low-level process
by which the memory used by objects which are no longer accessible is
reclaimed for use by new objects. Ordinarily, garbage collection is
invoked asynchronously as the need arises, so explicit calls to GC are
unnecessary. A side-effect of garbage collection is that any ports created
by OPEN which are both inaccessible and still open, are closed. Cf. 13.8.
(GC-STATS) -> undefined
Prints some statistics about the most recent garbage collection.
(GC-NOISILY?) -> boolean Settable
Switch, initially true. If true, then the garbage collector will print
messages when invoked. If false, it will do its work silently.
-115-
Appendix A A meta-circular evaluator
This appendix gives an annotated listing of an evaluator for T. This evaluator
is written in T (thus the adjective "meta-circular," since it employs the
notation it tries to describe) and attempts to continue in the spirit of
[STEELE78ART] and [MCCARTHY60]. It represents an attempt, short of writing a
denotational semantics, to provide a moderately precise description of T's
semantics.
This interpreter should also be of some interest simply as an extended example
of a program written in T.
It is important to know what this evaluator is not:
- It is not efficient. It expressly makes no effort at all to do
things in clever or efficient ways.
- It is not complete. It tries to define most of the special forms
that it uses, but does not define any primitive procedures such as
CAR or APPLY. However, any special forms that have been omitted are,
for the most part, trivially definable in terms of the special forms
here defined, as source-to-source transformations; in fact this is
the how the existing implementation works.
- It is not parameterized (that is, extensible). No means of passing
in syntax tables or adding syntactic extensions (DEFINE-SYNTAX) is
provided.
- It makes no attempt to detect or deal with error conditions, such as
unevaluable expressions, unbound variables, or wrong number of
arguments in calls.
EVALUATE, the universal function, computes the value of an expression in a
given variable environment. It dispatches to a specialist routine on the basis
of the syntax of the expression.
(DEFINE (EVALUATE EXP ENV)
(COND ((SYMBOL? EXP)
(VALUE EXP ENV))
((OR (NUMBER? EXP)
(CHARACTER? EXP)
(STRING? EXP))
EXP)
((PAIR? EXP)
(CASE (CAR EXP)
((QUOTE) (EVALUATE-QUOTE EXP ENV))
((IF) (EVALUATE-IF EXP ENV))
((BLOCK) (EVALUATE-BLOCK EXP ENV))
((LAMBDA) (EVALUATE-LAMBDA EXP ENV))
((DEFINE) (EVALUATE-DEFINE EXP ENV))
((LSET) (EVALUATE-LSET EXP ENV))
((SET) (EVALUATE-SET EXP ENV))
((OBJECT) (EVALUATE-OBJECT EXP ENV))
((LOCALE) (EVALUATE-LOCALE EXP ENV))
((LOCATIVE) (EVALUATE-LOCATIVE EXP ENV))
((COND) (EVALUATE-COND EXP ENV))
((AND) (EVALUATE-AND EXP ENV))
((OR) (EVALUATE-OR EXP ENV))
((LET) (EVALUATE-LET EXP ENV))
(ELSE (EVALUATE-CALL EXP ENV))))))
-116-
EVALUATE-CALL computes the value of a call to a procedure. The procedure and
arguments are evaluated recursively, and the procedure is invoked using APPLY
(assumed to be primitive; not defined by this Appendix).
(DEFINE (EVALUATE-CALL EXP ENV)
(APPLY (EVALUATE (CAR EXP) ENV)
(MAP (LAMBDA (ARG) (EVALUATE ARG ENV))
(CDR EXP))))
QUOTE, IF, and BLOCK have straightforward definitions.
(DEFINE (EVALUATE-QUOTE EXP ENV)
(CADR EXP))
(DEFINE (EVALUATE-IF EXP ENV)
(IF (EVALUATE (CADR EXP) ENV)
(EVALUATE (CADDR EXP) ENV)
(EVALUATE (CADDDR EXP) ENV)))
(DEFINE (EVALUATE-BLOCK EXP ENV)
(EVALUATE-SEQUENCE (CDR EXP) ENV))
EVALUATE-SEQUENCE is an auxiliary routine called in several places. It needs
to be careful to maintain tail-recursive semantics; thus the loop termination
when (NULL? (CDR EXPS)) instead of the more obvious (NULL? EXPS).
(DEFINE (EVALUATE-SEQUENCE EXPS ENV)
(COND ((NULL? (CDR EXPS))
(EVALUATE (CAR EXPS) ENV))
(ELSE
(EVALUATE (CAR EXPS) ENV)
(EVALUATE-SEQUENCE (CDR EXPS) ENV))))
Surprisingly, EVALUATE-LAMBDA has a very straightforward definition in terms of
LAMBDA: it simply returns a procedure which evaluates the body of the
lambda-expression in an appropriately augmented variable environment.
(DEFINE (EVALUATE-LAMBDA EXP ENV)
(LAMBDA ARGS
(EVALUATE-SEQUENCE (CDDR EXP)
(BIND-VARIABLES (CADR EXP) ARGS ENV))))
SET has two cases; whereas assignment to a variable must be primitive,
assignment to a generalized "place" is performed using a simple source-code
rewrite.
(DEFINE (EVALUATE-SET EXP ENV)
(LET ((PLACE (CADR EXP)))
(COND ((ATOM? PLACE)
(SET-VALUE PLACE ENV (EVALUATE (CADDR EXP) ENV) NIL))
(ELSE
(EVALUATE `((SETTER ,(CAR PLACE)) ,@(CDR PLACE) ,(CADDR EXP)
ENV)))))
(SET variable value) and (LSET variable value) are interpreted in exactly the
same way, except for the fourth argument passed to SET-VALUE, which is a flag
to be eventually passed to ENV-LOOKUP; this determines whether the lookup
proceeds outwards past the innermost locale, or stops there, where the variable
is either replaced or inserted in the environment. DEFINE is the same as LSET,
but permits extended syntax for defining procedures.
-117-
(DEFINE (EVALUATE-LSET EXP ENV)
(SET-VALUE (CADR EXP) ENV (EVALUATE (CADDR EXP) ENV) T))
(DEFINE (EVALUATE-DEFINE EXP ENV)
(LET ((PATTERN (CADR EXP)))
(COND ((ATOM? PATTERN)
(EVALUATE-LSET EXP ENV))
(ELSE
(EVALUATE-LSET `(LSET ,(CAR PATTERN)
(LAMBDA ,(CDR PATTERN) ,@(CDDR EXP)))
ENV)))))
The evaluator implements OBJECT using JOIN by iteratively constructing, one
method-clause at a time, an object which will have the requisite behavior. The
auxiliary routine EVALUATE-OBJECT-AUX could have been defined using LABELS, but
the code would have become too deeply indented, exacerbating this manual's
already forbidding formatting problems.
(DEFINE (EVALUATE-OBJECT EXP ENV)
(EVALUATE-OBJECT-AUX (EVALUATE (CADR EXP) ENV) (CDDR EXP) ENV))
(DEFINE (EVALUATE-OBJECT-AUX PROC CLAUSES ENV)
(COND ((NULL? CLAUSES)
(OBJECT PROC))
(ELSE
(LET ((CLAUSE (CAR CLAUSES)))
(JOIN (OBJECT PROC
(((EVALUATE (CAAR CLAUSE) ENV) SELF . ARGS)
(EVALUATE-SEQUENCE (CDR CLAUSE)
(BIND-VARIABLES (CDAR CLAU
(CONS SELF
ENV))))
(EVALUATE-OBJECT-AUX NIL (CDR CLAUSES) ENV))))))
As described elsewhere in this manual, a locale is an environment whose list of
bound variables can be mutated, in a controlled way, as a side-effect. To
accomplish this, LOCALE is implemented using a local variable THE-ENV which is
assigned new values (using a SET side-effect) as new variables are added to the
environment. This happens whenever a locative for a variable is needed and
cannot be located, either because the enclosing environment has no binding for
the variable or because the LOCAL? argument to ENV-LOOKUP is true and the
lookup must stop at the level of the locale.
The actual locale object has a trivial definition for the ENV-LOOKUP method
which jumps directly to whatever the current value of THE-ENV happens to be.
(DEFINE (EVALUATE-LOCALE EXP ENV)
(LET ((THE-ENV NIL))
(SET THE-ENV
(OBJECT NIL
((ENV-LOOKUP SELF ID LOCAL? CREATE?)
(COND ((AND LOCAL? (NOT CREATE?))
NIL)
((AND (NOT LOCAL?)
(ENV-LOOKUP ENV ID NIL NIL)))
(CREATE?
(SET THE-ENV
(BIND-VARIABLE ID (UNDEFINED-VALUE) THE-E
(ENV-LOOKUP THE-ENV ID LOCAL? CREATE?))
(ELSE NIL)))))
-118-
(LET ((THE-LOCALE
(OBJECT NIL
((ENV-LOOKUP SELF ID LOCAL? CREATE?)
(ENV-LOOKUP THE-ENV ID LOCAL? CREATE?)))))
(IF (CADR EXP) (SET-VALUE (CADR EXP) THE-LOCALE THE-LOCALE T))
(EVALUATE-SEQUENCE (CDDR EXP) THE-LOCALE))))
Environments are represented as objects which handle the ENV-LOOKUP operation.
When a procedure created by LAMBDA is called, BIND-VARIABLES is called to build
up a new environment structure, one variable at a time. This is analogous to
the association-list technique used in [STEELE78ART] and [MCCARTHY60], except
that the environment is represented not by list structure but by chained
closure (i.e. object) structure. (Compare this with the code for dynamic
binding in [STEELE76IMP], section 3.2.2.)
BIND-VARIABLES is a simple recursion over the lists of formal and actual
parameters, calling BIND-VARIABLE for each variable which needs to be bound.
If the list of formals is an improper list then the identifier which is its
last cdr must be bound to a list of the rest of the arguments.
(DEFINE (BIND-VARIABLES FORMALS ACTUALS ENV)
(COND ((PAIR? FORMALS)
(BIND-VARIABLE (CAR FORMALS)
(CAR ACTUALS)
(BIND-VARIABLES (CDR FORMALS)
(CDR ACTUALS)
ENV)))
((NULL? FORMALS)
ENV)
(ELSE
(BIND-VARIABLE FORMALS ACTUALS ENV))))
(DEFINE (BIND-VARIABLE IDENTIFIER VALUE ENV)
(OBJECT NIL
((ENV-LOOKUP SELF ID LOCAL? CREATE?)
(COND ((EQ? ID IDENTIFIER)
(LOCATIVE VALUE))
(ELSE
(ENV-LOOKUP ENV ID LOCAL? CREATE?))))))
Fetching and storing the value of a variable is accomplished by obtaining a
locative to the variable using the ENV-LOOKUP operation, and then either
examining or depositing into the locative.
(DEFINE (VALUE ID ENV)
(CONTENTS (ENV-LOOKUP ENV ID NIL NIL)))
(DEFINE (SET-VALUE ID ENV VAL LOCAL?)
(SET (CONTENTS (ENV-LOOKUP ENV ID LOCAL? T)) VAL))
EVALUATE-LOCATIVE, EVALUATE-COND, EVALUATE-OR, EVALUATE-AND, EVALUATE-CASE, and
EVALUATE-LET have been omitted to save space; their definitions are
uninteresting. Each could be done either as a primitive ("fexpr"), as e.g. IF
is defined above, or could be done using source-to-source transformations
(macros), using quasiquote and a single call to EVALUATE.
-119-
Appendix B Libraries
This appendix describes various software packages which are not officially part
of the T language but which are of general utility. Some are available within
the implementations (Tau) directly, while others must be loaded from external
files using LOAD.
B.1. The data base [REMOVED in T>3] X13
The data base is a global two-dimensional sparse table indexed by arbitrary
objects. Entries to the data base can be added, retrieved, and deleted via the
system procedures PROPERTY and REMOVE-PROPERTY.
By being a global resource, the data base violates a basic tenet of the T
design philosophy, that the system should have no global resources. Global
resources present modularity problems, because different programs may make
conflicting use of them. In the case of the data base, program A might make
use of data base entries whose second coordinate is the symbol FOO. If program
B also makes use of such data base entries, but does not know (or want to know)
that program A is already making use of those entries, then each program may
write over or be confused by the other's entries.
The data base corresponds to what is known in other Lisp dialects as "property
lists."
(PROPERTY object1 object2) -> object Settable
(GET object1 object2) -> object
Access the data base entry indexed by object1 and object2.
(SET (PROPERTY 'FOO 'COLOR) 'RED) => RED
(PROPERTY 'FOO 'COLOR) => RED
(SET (PROPERTY 'FOO 'COLOR) 'GREEN) => GREEN
(PROPERTY 'FOO 'COLOR) => GREEN
Bug: In Tau 2.7, object1 must be a symbol.
(SET-PROPERTY object1 object2 value) -> value
(PUT object1 object2 value) -> value
Sets the data base entry indexed by object1 and object2.
(SET-PROPERTY object1 object2 value)
<=> (SET (PROPERTY object1 object2) value)
Bug: In Tau 2.7, object1 must be a symbol.
(REMOVE-PROPERTY object1 object2) -> object or false
REMOVE-PROPERTY removes the data base entry indexed by object1 and
object2. It returns false if there was no such entry. (Note that there
is no way to distinguish the absence of an entry from an entry whose value
is false.)
-120-
B.2. Symbol tables [Removed from T>3] X13
(MAKE-SYMBOL string) -> symbol
Returns a new symbol whose print name is string.
A symbol table maps strings to symbols.
THE-SYMBOL-TABLE
This variable holds the value of the standard system symbol table. This
is used implicitly by STRING->SYMBOL and READ.
(MAKE-SYMBOL-TABLE size) -> symbol-table
Creates a new symbol table, suitable as argument to INTERN and other
symbol-table manipulation routines.
(INTERN string symbol-table) -> symbol
Looks in symbol-table for a symbol whose print name is STRING-EQUAL? to
string, and returns it if one exists. If not, it creates a new symbol,
whose print name is a copy of string, and returns it.
(REALLY-INTERN string symbol-table) -> symbol
Similar to INTERN, except that if a new symbol must be created, string
itself is used as the print name, not a copy of string.
(INTERNED string symbol-table) -> symbol or false
Looks in symbol-table for a symbol whose print name is STRING-EQUAL? to
string. Returns it if one exists, returns false otherwise.
(INTERNED? string symbol-table) -> boolean
(INTERNED? string symbol-table)
<=>
(TRUE? (INTERNED string symbol-table))
(WALK-SYMBOL-TABLE procedure symbol-table) -> undefined
Procedure is called on each symbol in symbol-table.
-121-
B.3. List utilities
(MEM predicate object list) -> list
Returns the first tail of list such that (predicate object (CAR tail)).
(MEM EQ? 'A '(B A D E)) => (A D E)
(MEMQ object list) -> list
(MEMQ object list) <=> (MEM EQ? object list)
(ANY predicate . lists) -> object
Calls the predicate on successive elements of the lists, stopping as soon
as the predicate returns a value other than false. This value is returned
as ANY's value.
(ANY NUMBER? '(A B 3 FOO)) => true
(ANY NUMBER? '(A B C FOO)) => false
(ANY < '(1 2 3) '(6 6 6)) => true
(ANY MEMQ '(A B C) '((X B) (U B Z) (C D E F))) => (B Z)
(ANYCDR predicate . lists) -> object
Like ANY, but the predicate is called on successive tails of the lists
instead of successive elements.
(EVERY predicate . lists) -> object
Calls the predicate on successive elements of the lists, stopping as soon
as the predicate returns false. If for every element predicate returns
true, EVERY returns the value of the last call.
(EVERY NUMBER? '(1 2 3 -15.7)) => true
(EVERY NUMBER? '(1 2 3 FOO)) => false
(EVERY ASSQ '(A B C)
'(((A X) (X Y)) ((A J) (B K)) ((C D) (T U))))
=> (C D)
(EVERYCDR predicate . lists) -> object
Like EVERY, but the predicate is called on successive tails of the lists
instead of successive elements.
(EVERYCDR? predicate . lists) -> boolean
(EVERYCDR? predicate . lists) <=> (TRUE? (EVERYCDR predicate . l
(ANYCDR? predicate . lists) -> boolean
(ANYCDR? predicate . lists) <=> (TRUE? (ANYCDR predicate . lists
-122-
(POS predicate object list) -> integer or false
Returns the position of the first item in list such that (predicate object
item), or false if there is no such item.
(POSQ object list) -> integer or false
(POSQ object list) <=> (POS EQ? object list)
(APPEND-REVERSE list ending) -> list
(APPEND-REVERSE list ending) <=>
(APPEND (REVERSE list) ending)
(APPEND-REVERSE '(A B C) '(D E)) => (C B A D E)
(APPEND-REVERSE! list ending) -> list
(APPEND-REVERSE! list ending) <=>
(APPEND! (REVERSE! list) ending)
B.4. Type-specific arithmetic
A fixnum is an integer whose magnitude lies within an implementation-dependent
range. This range is guaranteed to include all integers which are acceptable
as array, string, or vector indices. In Tau, this range is the half-open
-28 28
interval [2 ,2 ).
(FIXNUM? object) -> boolean Type predicate
Returns true if object is a fixnum.
MOST-POSITIVE-FIXNUM -> integer
Returns the largest integer value within the fixnum range. No integer
answering true to FIXNUM? is greater than this.
MOST-NEGATIVE-FIXNUM -> integer
Returns the smallest integer value within the fixnum range. No integer
answering true to FIXNUM? is less than this.
The following procedures are defined for performing type-restricted arithmetic.
These procedures should be considered specializations of their corresponding
generic arithmetic procedures. They assume restrictions on the types of their
arguments and results. The prefix FX means "fixnum-specific," and FL means
"flonum-specific." For example,
(FX+ x y) <=> (PROCLAIM FIXNUM? (+ (PROCLAIM FIXNUM? x) (PROCLAIM FIXNUM?y)))
-123-
Tau will implement calls to these type-specific procedures in a more efficient
manner than calls to the corresponding generic procedures.
The following list catalogs the available routines. In most case the effect of
the procedure should be analogous to the example above. The one exception is
FX/, which is a specialization of the truncated (integer) division routine
QUOTIENT, not of the division routine /.
(FX+ fixnum1 fixnum2) -> fixnum
(FL+ fixnum1 fixnum2) -> flonum
(FX- fixnum1 fixnum2) -> fixnum
(FL- flonum1 flonum2) -> flonum
(FX* fixnum1 fixnum2) -> fixnum
(FL* fixnum1 fixnum2) -> flonum
(FX/ fixnum1 fixnum2) -> fixnum
(FL/ flonum1 flonum2) -> flonum
(FX= fixnum1 fixnum2) -> boolean
(FL= flonum1 flonum2) -> boolean
(FX< fixnum1 fixnum2) -> boolean
(FL< flonum1 flonum2) -> boolean
(FX> fixnum1 fixnum2) -> boolean
(FL> flonum1 flonum2) -> boolean
(FXN= fixnum1 fixnum2) -> boolean
(FLN= flonum1 flonum2) -> boolean
(FX>= fixnum1 fixnum2) -> boolean
(FL>= flonum1 flonum2) -> boolean
(FX<= fixnum1 fixnum2) -> boolean
(FL<= flonum1 flonum2) -> boolean
(FIXNUM-REMAINDER fixnum1 fixnum2) -> fixnum
(FIXNUM-ODD? fixnum) -> boolean
(FIXNUM-EVEN? fixnum) -> boolean
(FIXNUM-ABS fixnum) -> fixnum
(FIXNUM-MIN fixnum1 fixnum2) -> fixnum
(FIXNUM-MAX fixnum1 fixnum2) -> fixnum
(FIXNUM-LOGAND fixnum1 fixnum2) -> fixnum
(FIXNUM-LOGIOR fixnum1 fixnum2) -> fixnum
(FIXNUM-LOGNOT fixnum) -> fixnum
(FIXNUM-ASHR fixnum) -> fixnum
(FIXNUM-ASHL fixnum) -> fixnum
(FIXNUM->FLONUM fixnum) -> flonum
(FLONUM->FIXNUM flonum) -> fixnum
Appendix C ASCII character conversion
The ASCII character set has nothing inherently to do with the T language or its
implementations. Internally, any implementation is free to use any convenient
encoding for characters, for example, a modified ASCII (such as that of the
Lisp Machine), or EBCDIC. However, the language does provide conversions
between characters and ASCII codes (CHAR->ASCII and ASCII->CHAR), so this
correspondence is, for the purposes of those routines, a part of the language
definition. The table below is also provided as a general reference, because
people using T are also likely to be using computers, and this information may
often be useful in that context.
-124-
8 10 16 charact| 8 10 16 charac| 8 10 16 chara| 8 10 16 char
- -- -- -------| - -- -- ------| - -- -- -----| - -- -- ----
0 0 0 #\NULL |40 32 20 #\SPAC|100 64 40 @ |140 96 60 `
1 1 1 |41 33 21 ! |101 65 41 A |141 97 61 a
2 2 2 |42 34 22 " |102 66 42 B |142 98 62 b
3 3 3 |43 35 23 # |103 67 43 C |143 99 63 c
4 4 4 |44 36 24 $ |104 68 44 D |144 100 64 d
5 5 5 |45 37 25 % |105 69 45 E |145 101 65 e
6 6 6 |46 38 26 & |106 70 46 F |146 102 66 f
7 7 7 #\BELL |47 39 27 ' |107 71 47 G |147 103 67 g
10 8 8 #\BACKS|50 40 28 ( |110 72 48 H |150 104 68 h
11 9 9 #\TAB |51 41 29 ) |111 73 49 I |151 105 69 i
12 10 A #\LINEF|52 42 2A * |112 74 4A J |152 106 6A j
13 11 B |53 43 2B + |113 75 4B K |153 107 6B k
14 12 C #\FORM |54 44 2C , |114 76 4C L |154 108 6C l
15 13 D #\RETUR|55 45 2D - |115 77 4D M |155 109 6D m
16 14 E |56 46 2E . |116 78 4E N |156 110 6E n
17 15 F |57 47 2F / |117 79 4F O |157 111 6F o
20 16 10 |60 48 30 0 |120 80 50 P |160 112 70 p
21 17 11 |61 49 31 1 |121 81 51 Q |161 113 71 q
22 18 12 |62 50 32 2 |122 82 52 R |162 114 72 r
23 19 13 |63 51 33 3 |123 83 53 S |163 115 73 s
24 20 14 |64 52 34 4 |124 84 54 T |164 116 74 t
25 21 15 |65 53 35 5 |125 85 55 U |165 117 75 u
26 22 16 |66 54 36 6 |126 86 56 V |166 118 76 v
27 23 17 |67 55 37 7 |127 87 57 W |167 119 77 w
30 24 18 |70 56 38 8 |130 88 58 X |170 120 78 x
31 25 19 |71 57 39 9 |131 89 59 Y |171 121 79 y
32 26 1A |72 58 3A : |132 90 5A Z |172 122 7A z
33 27 1B #\ESCAP|73 59 3B ; |133 91 5B [ |173 123 7B {
34 28 1C |74 60 3C < |134 92 5C \ |174 124 7C |
35 29 1D |75 61 3D = |135 93 5D ] |175 125 7D }
36 30 1E |76 62 3E > |136 94 5E ^ |176 126 7E ~
37 31 1F |77 63 3F ? |137 95 5F _ |177 127 7F #\RU
-125-
Appendix D Equivalences
This is a cross-index for names of some of the more common primitives from
other Lisp dialects which might be similar to those in T. In the left hand
column is a list of names of functions found in other Lisps, and in the center
are likely topics to look under in this document for similar functionality.
These function names are drawn from INTERLISP, Lisp 1.5, Lisp-Machine Lisp,
Maclisp, SCHEME, UCI LISP, and Common Lisp.
The more obvious equivalences, for example, predicates where a name ending in P
corresponds to a T name ending in ?, have been omitted.
For topic... Look under... On page...
ASSOC . . . . . . . . . ASS . . . . . . . . . 49
CASEQ . . . . . . . . . CASE . . . . . . . . . 19
CHAR-CODE . . . . . . . CHAR->ASCII . . . . . 67
CHRCT . . . . . . . . . HPOS . . . . . . . . . 93
CHRTAB . . . . . . . . PORT-READ-TABLE . . 93
CLOSURE . . . . . . . . LAMBDA . . . . . . . . 8
CODE-CHAR . . . . . . . ASCII->CHAR . . . . . 67
CONCAT . . . . . . . . CONCATENATE-SYMBOL . . 71
CONSP . . . . . . . . . PAIR? . . . . . . . . 43
CURSORPOS . . . . . . . HPOS . . . . . . . . . 93
or VPOS . . . . . . . . . 93
DEFFLAVOR . . . . . . . OBJECT . . . . . . . . 31
DEFMACRO . . . . . . . DEFINE-SYNTAX . . . . 83
DEFMETHOD . . . . . . . OBJECT . . . . . . . . 31
DEFPROP . . . . . . . . PUT . . . . . . . . . 120
DEFSTRUCT . . . . . . . DEFINE-STRUCTURE-TYPE 55
DEFSUBST . . . . . . . DEFINE-INTEGRABLE . . 71
DEFUN . . . . . . . . . DEFINE . . . . . . . . 15
DEFVAR . . . . . . . . LSET . . . . . . . . . 15
DEFVST . . . . . . . . DEFINE-STRUCTURE-TYPE 55
DEF-OPEN-CODED . . . . DEFINE-INTEGRABLE . . 71
DELQ . . . . . . . . . DELQ! . . . . . . . . 47
DE . . . . . . . . . . DEFINE . . . . . . . . 15
DF . . . . . . . . . . DEFINE-SYNTAX . . . . 83
DIFFERENCE . . . . . . SUBTRACT . . . . . . . 37
DM . . . . . . . . . . DEFINE-SYNTAX . . . . 83
DREMOVE . . . . . . . . DEL! . . . . . . . . . 47
DREVERSE . . . . . . . REVERSE! . . . . . . . 47
DSKIN . . . . . . . . . LOAD . . . . . . . . . 102
DSKLOG . . . . . . . . TRANSCRIPT-ON . . . . 108
DSKOUT . . . . . . . . WITH-OPEN-STREAMS . . 87
ENDP . . . . . . . . . NULL-LIST? . . . . . . 43
EQUAL . . . . . . . . . ALIKEV? . . . . . . . 51
EXPANDMACRO . . . . . . MACRO-EXPAND . . . . . 86
EXPLODE . . . . . . . . STRING->LIST . . . . . 63
FIXP . . . . . . . . . INTEGER? . . . . . . . 36
FLATC . . . . . . . . . DISPLAYWIDTH . . . . . 93
FLATSIZE . . . . . . . PRINTWIDTH . . . . . . 93
FUNCTION . . . . . . . LAMBDA . . . . . . . . 6
GENSYM . . . . . . . . GENERATE-SYMBOL . . . 71
GETCHARN . . . . . . . STRING-ELT . . . . . . 64
GETCHAR . . . . . . . . STRING-ELT . . . . . . 64
GET-PNAME . . . . . . . SYMBOL->STRING . . . . 68
-126-
INTERN . . . . . . . . STRING->SYMBOL . . . . 68
IOTA . . . . . . . . . WITH-OPEN-STREAMS . . 87
LABEL . . . . . . . . . LABELS . . . . . . . . 10
LAST . . . . . . . . . LASTCDR . . . . . . . 45
LIST* . . . . . . . . . CONS* . . . . . . . . 45
LITATOM . . . . . . . . SYMBOL? . . . . . . . 6
LOCF . . . . . . . . . LOCATIVE . . . . . . . 27
MAKNAM . . . . . . . . STRING->SYMBOL . . . . 68
MAPATOMS . . . . . . . WALK-SYMBOLS . . . . . 115
MAPCAR . . . . . . . . MAP . . . . . . . . . 48
MAPC . . . . . . . . . WALK . . . . . . . . . 48
MAPLIST . . . . . . . . MAPCDR . . . . . . . . 48
MAP . . . . . . . . . . WALKCDR . . . . . . . 48
MCONS . . . . . . . . . CONS* . . . . . . . . 44
MEMBER . . . . . . . . MEM . . . . . . . . . 122
MINUSP . . . . . . . . NEGATIVE? . . . . . . 39
MINUS . . . . . . . . . NEGATE . . . . . . . . 37
MSG . . . . . . . . . . FORMAT . . . . . . . . 92
NCONC . . . . . . . . . APPEND! . . . . . . . 46
NCONS . . . . . . . . . LIST . . . . . . . . . 44
NRECONC . . . . . . . . APPEND-REVERSE! . . . 123
NREVERSE . . . . . . . REVERSE! . . . . . . . 46
NSUBSTRING . . . . . . STRING-SLICE . . . . . 64
NTH . . . . . . . . . . NTHCDR . . . . . . . . 45
NUMBERP . . . . . . . . NUMBER? . . . . . . . 36
PLUSP . . . . . . . . . POSITIVE? . . . . . . 39
PLUS . . . . . . . . . ADD . . . . . . . . . 37
PNGET . . . . . . . . . SYMBOL->STRING . . . . 68
PRINC . . . . . . . . . DISPLAY . . . . . . . 91
PRINC . . . . . . . . . WRITES . . . . . . . . 90
PRIN1 . . . . . . . . . PRINT . . . . . . . . 90
or WRITE . . . . . . . . 90
PROGN . . . . . . . . . BLOCK . . . . . . . . 23
PROG1 . . . . . . . . . BLOCK0 . . . . . . . . 23
PUTPROP . . . . . . . . PUT . . . . . . . . . 120
QUOTIENT . . . . . . . DIVIDE . . . . . . . . 37
READCH . . . . . . . . READC . . . . . . . . 88
READLINE . . . . . . . READ-LINE . . . . . . 89
RECORD-TYPE . . . . . . DEFINE-STRUCTURE-TYPE 55
REMOVE . . . . . . . . DEL . . . . . . . . . 47
REMQ . . . . . . . . . DELQ . . . . . . . . . 47
RETURN . . . . . . . . CATCH . . . . . . . . 23
RPLACA . . . . . . . . CAR . . . . . . . . . 44
RPLACD . . . . . . . . CDR . . . . . . . . . 44
SELECTQ . . . . . . . . CASE . . . . . . . . . 19
SETF . . . . . . . . . SET . . . . . . . . . 25
SETQ . . . . . . . . . SET . . . . . . . . . 25
or LSET . . . . . . . . . 15
SOME . . . . . . . . . ANYCDR . . . . . . . . 122
SPECIAL . . . . . . . . BIND . . . . . . . . . 28
SPRINT . . . . . . . . PRETTY-PRINT . . . . . 91
SPRINTER . . . . . . . PRINT . . . . . . . . 91
SUBST . . . . . . . . . SUBSTV . . . . . . . . 52
SUB1 . . . . . . . . . SUBTRACT1 . . . . . . 37
TAB . . . . . . . . . . HPOS . . . . . . . . . 93
TERPRI . . . . . . . . FRESH-LINE . . . . . . 91
or NEWLINE . . . . . . . 91
THROW . . . . . . . . . CATCH . . . . . . . . 23
TIMES . . . . . . . . . MULTIPLY . . . . . . . 37
TTYMSG . . . . . . . . FORMAT . . . . . . . . 92
TYIPEEK . . . . . . . . PEEKC . . . . . . . . 89
TYI . . . . . . . . . . READC . . . . . . . . 89
TYO . . . . . . . . . . WRITEC . . . . . . . . 90
-127-
ZEROP . . . . . . . . . ZERO? . . . . . . . . 39
1- . . . . . . . . . . SUBTRACT1 . . . . . . 37
\ . . . . . . . . . . . REMAINDER . . . . . . 37
// . . . . . . . . . . QUOTIENT . . . . . . . 37
*CATCH . . . . . . . . CATCH . . . . . . . . 23
*THROW . . . . . . . . CATCH . . . . . . . . 23
:= . . . . . . . . . . SET . . . . . . . . . 25
or MODIFY . . . . . . . . 25
$EOF$ . . . . . . . . . EOF . . . . . . . . . 88
^G . . . . . . . . . . RESET . . . . . . . . 107
-128-
Appendix E Friendly advice
E.1. Comparison with other Lisp dialects
Some of the terminology may take some getting used to. We always say procedure
instead of function, for example. Special form and reserved word have special
meaning. Syntax usually refers not to what the reader does but to what the
compiler does.
As in Common Lisp, but unlike most familiar Lisp dialects besides Scheme, T is
lexically scoped.
As in Scheme, there is full support for lexical closures, and tail-recursive
calls are reliably performed as jumps.
Generic operations are like message-passing. JOIN can be used to implement
object systems analogous to the Lisp Machine's flavor system.
Synonyms generalize Lisp Machine Lisp's MAKE-SYN-STREAM.
See also the equivalences appendix for some rough functional analogues.
E.2. Incompatibilities
The empty list is distinguished from the symbol whose print name is "NIL".
That is, (NEQ? NIL 'NIL). The value of the variable NIL is the empty list.
The empty list is the same as the logical false value.
All "global" variables must be declared using LSET before they are assigned
using SET or BIND. This is quite unlike most Lisp dialects where the first
SETQ causes a variable to come into existence.
T has no FEXPR or NLAMBDA mechanism. Their effect may be accomplished using
procedures or macros.
There are no SPECIAL declarations and no implicit dynamic binding. BIND must
be used explicitly when a variable is to be dynamically bound.
COND and CASE don't yield nil in the fall-through case; they yield undefined
values.
RETURN and GO-tags are not supported in DO. Lisp RETURN may be simulated using
(CATCH RETURN ...).
NTH is incompatible with Maclisp's function of the same name. Maclisp's takes
the index as the first argument and the list as the second, instead of the
other way around.
LAST returns the last element of the list, not the last pair, as in Maclisp.
Maclisp's LAST is like T's LASTCDR.
APPEND isn't defined to copy all but the last list. I.e. the language
definition allows the implementation to yield X, and not a copy of it, for
(APPEND X '()).
PUSH's syntax is incompatible with that of PUSH in Maclisp and Common Lisp.
Locatives aren't as cheap as they are in Lisp Machine Lisp; creating a locative
may involve consing.
-129-
Appendix F Future work
This appendix catalogs some ideas about T's future development. It is provided
as a stimulus for user input, and as an assurance that these problems are known
and are being addressed.
F.1. Language design problems
A system for managing multiple namespaces -- loading files into appropriate
environments, and communicating names from one module to another in a
controlled way -- is sorely needed.
Better aggregate structures are needed (arrays, hash tables).
Read macro procedures ought to take a read table as an argument.
A condition/error signalling system is needed.
Need to make up for the loss of Scheme's general CATCH by implementing
coroutines. Indeterminacy, timeslicing, and synchronization primitives would
be nice also.
Each special form should be marked as being either primitive or a macro. This
would allow users to write robust program-manipulating programs using only the
released language.
There should be a way to remove bindings from locales.
There ought to be able to have variables named by objects other than symbols.
Maybe there ought to be a generalized INTERN which creates a unique instance of
any object, or something like OWL's UCONS (unique cons) primitive.
Should reintroduce DEFINE-LOCALE?
MEMQ, ASSQ, POSQ, etc. are inconsistent with the other aggregate accessing
routines in that the aggregate argument follows the index argument and not vice
versa.
Ideas for list routines: SUBSET, FILTER, FIND, set operations.
Need a term more specific than "tree" for whatever those things are.
The tree manipulation routines (e.g. ALIKEV?) should be user-extensible. Big
problem with what TREE-HASH should do with unusual leaf nodes.
There should be a general way to do generic dispatches based on more than one
argument.
There should be a non-side-effecting way to define methods for structures.
SYNONYM is a misleading name.
SETTER is inadequate. Why not PUSHER, SWAPPER, etc.? Is there any way to do
this that's at the same time convenient, general, and efficient?
CASE and SELECT should use EQUIV? instead of EQ?.
-130-
Method-clauses aren't incrementally redefinable. What to do about this.
Objects handling many operations become quite unmanageable. Maybe this is an
implementation and editor problem, not a language problem.
The looping/iteration constructs are maybe a little too simple. Figure out
some better ones without compromising principles, if possible. Waters' LetS
looks sort of good.
CHECK-ARG is ad hoc. Need type inference and other compiler smarts. Need
better type declaration syntax.
There should be case-ignoring character and string comparison predicates.
STRING-POSQ is not a good name.
STRING-REPLACE should probably be called STRING-REPLACE!, or flushed.
Similarly with VECTOR-FILL, VECTOR-REPLACE.
The name DIGIT? is nconsistent with DIGIT->CHAR -- does the term digit mean a
character or an integer?
What about UPPERCASE? -> UPPER-CASE?? Ugh.
Enumerated types.
Infinities?
Should NOT-ZERO? and friends be renamed to be NONZERO? etc.?
Divulge expression syntax for quasiquote?
Release WALK-WEAK-SET? It's redundant, but handy.
Maybe release BOUND?.
Need a way to make read tables be read-only.
Need LET-SYNTAX-TABLE and LET-READ-TABLE features?
Maybe flush the random not-parsable-as-number-implies-symbol syntax feature.
It can be a real pain to catch typos in numbers.
I/O system improvements needed:
- Ports are pretty random. How does a port differ from a sequence?
There should be ways to coerce from ports to (infinite) sequences,
and vice versa. Maybe the term port should follow Sussman's usage
(infinte sequence), not Common Lisp's (pointer into same).
- The FORMAT sublanguage is random and unextensible.
-131-
- Block-mode and/or "binary" I/O, for database or whatever
applications.
- There ought to be a way to make the printer complain if it comes
across an object which isn't re-readable.
- Ought to be a way to establish the line-length of a port.
- HPOS and VPOS aren't precisely defined.
- SPACE operation needs a better definition.
Structure package improvements needed:
- DEFINE-STRUCTURE-TYPE features: initializers, variant record types,
arguments to constructor procedure, type-restricted fields, alternate
name construction.
- Maybe structures should have an official external syntax.
- Structures ought to be callable.
- COPY-STRUCTURE and COPY-STRUCTURE! ought to take as an additional
argument the structure type to which the structure belongs.
Alternatively, a copying procedure could be associated with the
structure type object itself.
- Maybe there ought to be a way to test initializedness of structure
components.
F.2. Common Lisp influence
The design and implementation of T began around the same time that the Common
Lisp design effort started in earnest. Some of the goals of the projects have
been similar; in many cases, the T designers left certain problems for the
Common Lisp designers to work out, intentionally ignoring many sticky issues,
such as numeric routines, sequences, and arrays, in order to work on other
areas.
Now that the Common Lisp language has matured, and portable implementations are
approaching reality, two distinct integration projects are in order: importing
ideas and facilities from Common Lisp into T (with appropriate customization);
and building a Common Lisp emulation package in T.
T itself will never contain Common Lisp as a subset, but it should be suitable
as an implementation language for a Common Lisp. This will probably take the
form of alternate evaluator, reader, printer, and namespace, together with a
Common Lisp-to-T translator.
The following Common Lisp features, at least, should be incorporated into T in
some form:
- Arrays.
- Sequences. (These would clean up a lot of the current clutter with
the string and vector routines, but introduce a new level of
implementation hair.)
- Multiple values. These can be simulated now with continuation-
passing, but the syntax is clumsy.
-132-
- Numeric routines. Ratios and complexes. Multiple floating point
precisions.
- SHIFTF and ROTATEF. (These are generalized versions of T SWAP and
EXCHANGE.)
- Hash tables.
- FORMAT enhancements: floating-point formats, etc.
- File dates. Time and date manipulation. Time and date parsing.
F.3. Bugs in the implementation
The implementation (Tau 2.7) doesn't implement everything described in this
manual.
- Not implemented: ATAN2, JOIN, ROUND, PORT-POSITION, and TRUNCATE.
Many things are implemented incorrectly.
- Deficiencies in syntax environments (they don't exist per se) and in X3,&c.
locales (shadowing loses). X8,&c.
- GENERATE-SYMBOL: The implementation generates obscure names, but
doesn't guarantee uniqueness. That is, the symbols are interned.
- ANY? and EVERY? of more than one list don't work.
- GET and PUT work only on symbols.
- READ-LINE and UNREAD-CHAR interact badly.
- There is no way to trace an operation.
Many things are implemented less efficiently than they ought to be.
- The garbage collector is slow. This should be fixed.
- There ought to be an option to invoke the standard compiler on
demand, as code is run, instead of all at once, as code is loaded.
- BIND is implemented in a pretty cons-intensive way; there's a bigger
performance penalty for dynamic binding than one would find in other
Lisp implementations.
- "Lexprs" are inefficient because they always cons a list for the
rest-variable.
- The i/o system doesn't do any buffering; this really slows down
reading and printing a lot, most notably the loading of code files.
- Operation dispatch could be done using hash tables. DEFINE-OPERATION
needs to do early binding. This is feasible, but the implementation
could become quite hairy.
- Locales are permanent (!), and forward references to shadowed
variables will lose big, especially with the standard compiler.
-133-
- The interpreter conses a lot more than it ought to. It ought to do
some small amount of closure analysis, or else use McDermott's
dynamic migration hack.
- The combinator routines are pretty useless since TC doesn't know
anything about them. This should be fixed. For example, ((COMPOSE
CAR CDR) '(A B C)) is evaluated, even in compiled code, by actually
consing a procedure, and then invoking it.
Many things in the system are implemented less smoothly than they ought to be.
- Many system routines still don't check their argument types, for
example LOWERCASE? and UPPERCASE?.
- Redefining DEFINE-CONSTANT'ed and DEFINE-INTEGRABLE'd should report
an error condition.
- Newline characters following input lines come out at the wrong place
in transcript files.
- LOAD ought to be smarter about file dates, checking for
recompilation, etc.
- Interpreted QUOTE ought to enforce read-only-ness.
- There ought to be a way to enforce the downwards-only-ness of escape
procedures.
Missing features.
- SUSPEND and/or clean embedded-system-building tools.
- Help system.
- More system self-knowledge: who calls x, who does x call, type
analysis, etc. Masterscope-like stuff.
- Block compilation.
- There should be a version of TRACE which destructively modifies a
procedure so that it acts traced.
-134-
References
-135-
Index
## p106 / p37
' p7 + p37
* p37 < p37
*AND p20 <0? p39
*COMMAND-LINE* p106 <= p39
*DEFINE p16 <=0? p39
*IF p20 <=> p1
*LSET p16
ABS p37
*NUMBER-OF-CHAR-CODES* ACOS p39
*OR p20 ADD p37
*VALUE p16 ADD-TO-WEAK-SET p76
ADD1 p37
+ p37 AEGIS-FS? p95
ALIKE? p51
- p37 ALIKEQ? p51
-1+ p37 ALIKEV? p51
-> p1 ALPHABETIC? p62
->FILENAME p95 ALWAYS p71
->FLOAT p39 AND p20
->INTEGER p39 ANY p122
------------------------------------------------------------------------------
ANY? p47 BIND p28
ANYCDR p122 BIT-FIELD p39
ANYCDR? p122 BLOCK p23
APPEND p46 BLOCK0 p23
APPEND! p47 BOOLEAN? p73
APPEND-REVERSE p122 BREAKPOINT p106
APPEND-REVERSE! p122 Backquote p53,p77
APPLY p23 Bindings p8f
ARGSPECTRUM p114
ASCII codes p125 C...R p46
ASCII->CHAR p67 CAR p46
ASH p39 CASE p20
ASIN p39 CATCH p23
ASS p47 CDR p46
ASSERT p71 CHAR p62
ASSQ p47 CHAR->ASCII p67
ATAN2 p39 CHAR->DIGIT p67
ATOM? p46 CHAR->STRING p62
Access routines p1 CHAR-DOWNCASE p62
Application p8 CHAR-UPCASE p62
Arguments p8 CHAR< p62
Assembly files p103 CHAR<= p62
Association list p47 CHAR= p62
CHAR> p62
BACKTRACE p110 CHAR>= p62
-136-
CHAR? p62 COS p39
CHARN= p62 CRAWL p110
CHDR p62 Call-by-name p33
CHDR! p62 Call-by-need p23
CHECK-ARG p71 Calling p4
CHOPY p62 Calls p4,p23
CHOPY! p62 Characters p77
CLEAR-INPUT p91 Closure p7
CLOSE p89 Combinators p71
COMFILE p103 Command level p106
COMMENT p71 Commands p106
COMPLEMENT p73 Comments p77
COMPOSE p73 Compilers p101
CONCATENATE-SYMBOL p71 Conditionals p5,p20
COND p20 Constituent characters p77
CONJOIN p73 Continuations p110
CONS p46 Contours p8f
CONS* p46 Core language p4
CONTENTS p27
-------------------------------------------------------------------------------
COPY-LIST p46 DEBUG p110
COPY-STRING p62 DEBUG-OUTPUT p89
COPY-STRUCTURE p57 DECREMENT p42
COPY-STRUCTURE! p59 DEFINE p16
COPY-TREE p51 DEFINE-CONSTANT p71
COPY-VECTOR p73 DEFINE-INTEGRABLE p71
DEFINE-LOCAL-SYNTAX p86 Default methods p30
DEFINE-OPERATION p33 Delays p23
DEFINE-PREDICATE p33 Delimiter characters p77
DEFINE-SETTABLE-OPERATION p33 Destructive p47
DEFINE-STRUCTURE-TYPE p57 Dot-notation p1
DEFINE-SYNTAX p80 Dynamic binding p27
DEL p47 Dynamic state p23,p27
DEL! p47
DELAY p23 ELSE p11
DELIMITING-READ-MACRO? p80 ENV p101
DELQ p47 ENV-SYNTAX-TABLE p80
DELQ! p47 EOF p89
DESTRUCTURE p51 EOF? p89
DESTRUCTURE* p51 EQ? p7
DIGIT p67 EQUAL? p37
DIGIT->CHAR p67 EQUIV? p51
DIGIT? p62 ERROR p71
DISCLOSE p114 ERROR-OUTPUT p89
DISJOIN p73 EVAL p80
DISPLAY p91 EVEN? p37
DISPLAYWIDTH p94 EVERY p122
DIV p37 EVERY? p47
DIVIDE p37 EVERYCDR p122
DO p23 EVERYCDR? p122
Data base p122 EXCHANGE p27
-137-
EXIT p106 FILENAME-NAME p95
EXP p39 FILENAME-TYPE p95
EXPT p37 FILENAME? p95
Early binding p71,p101 FIXNUM->FLONUM p124
Early bindings. p101 FIXNUM-ABS p124
Empty list p7 FIXNUM-ASHL p124
End-of-file p106 FIXNUM-ASHR p124
Environments p4,p8f FIXNUM-EVEN? p124
Equality predicates p5,p51 FIXNUM-LOGAND p124
Errors p69,p110 FIXNUM-LOGIOR p124
Evaluation p4,p82 FIXNUM-LOGNOT p124
Evaluator p4 FIXNUM-MAX p124
Expression syntax p4,p80 FIXNUM-MIN p124
Expressions p4 FIXNUM-ODD? p124
External representation p4,p77 FIXNUM-REMAINDER p124
FIXNUM? p124
FALSE p73 FL* p124
FALSE? p20 FL+ p124
FILE-DELETE p99 FL- p124
FILE-EXISTS? p99 FL/ p124
FILE-MOVE p99 FL< p124
FILENAME->STRING p95 FL<= p124
FILENAME-DIR p95 FL= p124
FILENAME-FS p95 FL> p124
FILENAME-GENERATION p95 FL>= p124
-------------------------------------------------------------------------------
FLN= p124 Frames p110
FLOAT? p37 Freelists p73
FLONUM->FIXNUM p124 Functions p107
FORCE p24 Functions, see Procedures
FORCE-OUTPUT p91
FORMAT p91 GC p114
FRESH-LINE p91 GC-NOISILY? p114
FX* p124 GC-STATS p114
FX+ p124 GCD p37
FX- p124 GOBENERATE-SYMBOL p71
FX/ p124 GET p122
FX< p124 GET-ENVIRONMENT p114
FX<= p124 GRAPHIC? p62
FX= p124 GREATER? p37
FX> p124 Garbage collection p75f,p114
FX>= p124
-138-
FXN= p124 HPOS p91
False p7 Handlers p30
File system objects p95 Hash code p51
File systems p95 Hexadecimal p77,p91
Filenames p95
Files p95,p99 IDENTIFICATION p114
Filespecs p95 IDENTITY p71
Fixnums p124 IF p20
Forms p4 IGNORABLE p71
IGNORE p71 LABELS p8f
IMPORT p16 LAMBDA p7
INCREMENT p39 LAST p46
INPUT-STREAM? p89 LASTCDR p46
INTEGER? p37 LENGTH p46
INTERACTIVE-STREAM? p89 LESS? p37
INTERN p122 LET p8f
INTERNED p122 LET* p8f
INVOKE-MACRO-EXPANDER p86 LET-SYNTAX p86
ITERATE p23 LINE-LENGTH p91
Identifiers p8f LIST p46
Identity p7 LIST->STRING p62
Implementation environment p101 LIST->VECTOR p73
Initialization file p106 LIST-TERMINATOR p81
Inspector p110 LIST? p46
Interactive p89 LOAD p101
Interrupts p106 LOAD-NOISILY? p108
Invoking p4 LOCAL-FS p95
Iteration p20 LOCALE p16
LOCALE? p16
JOIN p33 LOCATIVE p27
Joined objects p33 LOCATIVE? p27
LOG p39
KWOTE p86 LOGAND p39
LOGIOR p39
-------------------------------------------------------------------------------
LOGNOT p39 MAKE-WEAK-SET p76
LOGXOR p39 MAKE-READ-TABLE p77
LOWERCASE? p62 MAKE-STRING p62
LSET p16 MAKE-STYPE p57
Lazy evaluation p23 MAKE-SYMBOL p122
Lexical scoping p7 MAKE-SYMBOL-TABLE p122
Lists p4 MAKE-SYNTAX-TABLE p80
Literals p4,p7 MAKE-VECTOR p73
Loading p101 MAP p47
Local file system p95 MAP! p47
Local syntax p86 MAP-STRING p62
Locales p14f,p100 MAP-STRING! p62
Locations p27 MAPCDR p47
Locatives p27 MAX p37
Loops p20 MAYBE-OPEN p99
MEM p122
MACRO-EXPAND p86 MEM? p47
MACRO-EXPANDER p86 MEMQ p122
MACRO-EXPANDER? p86 MEMQ? p47
MAKE-BROADCAST-STREAM p94 MIN p37
MAKE-EMPTY-LOCALE p16 MOD p37
MAKE-FILENAME p95 MODIFY p27
MAKE-LIST-READER p80 MODIFY-LOCATION p27
MAKE-LOCALE p16 MOST-NEGATIVE-FIXNUM p124
MAKE-POOL p73 MOST-POSITIVE-FIXNUM p124
-139-
MULTIPLY p37 NULL? p46
Macro expanders p80 NUMBER? p37
Macros p84,p114 Noise files p103
Methods p30 Null p7
Numbers p4,p36,p77
N= p37
N=0? p39 OBJECT p30
NEGATE p37 OBJECT-HASH p76
NEGATIVE? p39 OBJECT-UNHASH p76
NEQ? p7 OBTAIN-FROM-POOL p73
NEWLINE p91 ODD? p37
NIL p7 OPEN p99
NOT p20 OPERATION p30
NOT-EQUAL? p37 OPERATION? p30
NOT-GREATER? p39 OR p20
NOT-LESS? p39 OUTPUT-STREAM? p89
NOT-NEGATIVE? p39 Object files p103
NOT-POSITIVE? p39 Objects p4f,p30
NOT-ZERO? p39 Octal p77,p91
NOTHING-READ p80 Operations p30
NTH p46
NTHCDR p46
NTHCHAR p62 PAIR? p46
NTHCHDR p62 PEEK-CHAR p89
NTHCHDR! p62 PEEKC p89
NULL-LIST? p46 POP p49
-------------------------------------------------------------------------------
POS p122 QUOTE p6,p77
POSITIVE? p39 QUASIQUOTE p53,p77
POSQ p122
PP p110 RATIO? p37
PRETTY-PRINT p91 READ p89
PRINT p91 READ-CHAR p89
PRINTWIDTH p91 READ-ERROR p71
PROCEDURE? p23 READ-LINE p89
PROCLAIM p71 READ-OBJECT p77
PROJ0 p71 READ-OBJECTS-FROM-STRING p91
PROJ1 p73 READ-REFUSING-EOF p91
PROJ2 p73 READ-TABLE p101
PROJ3 p73 READ-TABLE-ENTRY p80
PROJN p71 READC p89
PROPER-LIST? p46 REALLY-INTERN p122
PROPERTY p122 RECKLESSNESS p110
PUSH p47 REMAINDER p37
PUT p122 REMOVE-FROM-WEAK-SET p76
Parentheses p77 REMOVE-PROPERTY p122
Patch file p106 REPL-ENV p106
Pools p73 REPL-EVAL p108
Porphyrry p0 REPL-PRINT p108
Predicates p7 REPL-PROMPT p108
Procedures p5,p23 REPL-READ p106
-140-
REPL-WONT-PRINT p108 STANDARD-COMPILER p80
REPL-WONT-PRINT? p108 STANDARD-ENV p101
RESET p106 STANDARD-INPUT p89
RET p106 STANDARD-OUTPUT p89
RETURN-TO-POOL p76 STANDARD-READ-TABLE p77
REVERSE p47 STANDARD-SYNTAX-TABLE p80
REVERSE! p47 STOP p106
RUN-COMPILED-CODE p80 STREAM-FILENAME p99
Read macros p77 STREAM-READ-TABLE p91
Read tables p77 STREAM? p89
Read-eval-print loop p106 STRING->INPUT-STREAM p89
Reader p77 STRING->LIST p62
Reserved word p4 STRING->SYMBOL p67
Reserved words p80 STRING-APPEND p62
Routines p7 STRING-DOWNCASE p62
STRING-DOWNCASE! p67
SELECT p20 STRING-ELT p62
SELECTOR-ID p57 STRING-EMPTY? p62
SET p27 STRING-EQUAL? p62
SET-BIT-FIELD p39 STRING-HEAD p62
SET-PROPERTY p122 STRING-LENGTH p62
SETTER p27 STRING-NTHTAIL p62
SIN p39 STRING-NTHTAIL! p62
SPACE p91 STRING-POSQ p62
SQRT p39 STRING-REPLACE p62
-------------------------------------------------------------------------------
STRING-SLICE p62 SYNONYM p33
STRING-TAIL p62 SYNTAX-ERROR p71
STRING-TAIL! p62 SYNTAX-TABLE p101
STRING-UPCASE p62 SYNTAX-TABLE-ENTRY p80
STRING-UPCASE! p62 Scope p6,p8f
STRING? p62 Scratch environment p101
STRUCTURE-TYPE p114 Semantics p4
STRUCTURE? p57 Settable p1
STYPE-CONSTRUCTOR p57 Shadowing p8f
STYPE-HANDLER p57 Side-effects p4,p27
STYPE-ID p57 Source files p101
STYPE-MASTER p57 Special forms p4,p80
STYPE-PREDICATOR p57 Stack p110
STYPE-SELECTOR p57 Standard environment p4,p101
STYPE-SELECTORS p57 State p4,p27
SUBLIST p47 Streams p89
SUBST p51 String headers p62
SUBSTQ p51 String texts p62
SUBSTRING p62 Strings p77
SUBSTV p51 Structure types p57
SUBTRACT p37 Structures p57
SUBTRACT1 p37 Stype p57
SWAP p27 Support environments p101
SYMBOL->STRING p67 Support files p103
SYMBOL? p7 Symbol tables p122
-141-
Symbols p4,p6,p68,p77 True p7
Synonyms p33 Truth values p7
Syntax descriptors p80 Type predicates p7
Syntax tables p77,p80 Types p8,p71
T p8 UNDEFINED-EFFECT p71
T-IMPLEMENTATION-ENV p114 UNDEFINED-VALUE p71
T-VERSION-NUMBER p115 UNIX-FS? p95
TAN p39 UNREAD-CHAR p89
TC-ENV p115 UNREADC p89
TC-MACRO-DEFINITION-ENV p104 UNTRACE p110
TC-SYNTAX-TABLE p104 UNWIND-PROTECT p29
TERMINAL-INPUT p89 UPPERCASE? p62
TERMINAL-OUTPUT p89 USER-ENV p101
THE-SYMBOL-TABLE p122 Undefined p1f,p71
TRACE p110 Undefined effects p101
TRANSCRIPT-OFF p106
TRANSCRIPT-ON p106 VANILLA-READ-TABLE p77
TREE-HASH p51 VECTOR->LIST p73
TRUE p73 VECTOR-ELT p73
TRUE? p73 VECTOR-FILL p73
Tests p7 VECTOR-LENGTH p73
Throws p23 VECTOR-POS p73
Transcript p106 VECTOR-POSQ p73
Trees p51 VECTOR-REPLACE p73
-------------------------------------------------------------------------------
VECTOR? p73 WITH-OUTPUT-TO-STRING p89
VMS-FS? p95 WITH-OUTPUT-WIDTH-STREAM p91
VPOS p91 WRITE p91
VREF p73 WRITE-CHAR p91
VSET p73 WRITE-LINE p91
Values p8f WRITE-SPACES p91
Variables p4,p8f WRITE-STRING p91
Vectors p77 WRITEC p91
Version numbers p106 WRITES p91
Weak pointers p76
WALK p47 Weak sets p76
WALK-STRING p62 Weight p67
WALK-SYMBOL-TABLE p122 Whitespace? p61,p77
WALK-SYMBOLS p114
WALK-VECTOR p73 XCASE p18
WALK-WEAK-SET p76 XCOND p18
WALKCDR p47 XSELECT p19
Weak-sets p76
WEAK-SET->LIST p76
WHERE-DEFINED p114 Yield p4
WHITESPACE? p62
WITH-INPUT-FROM-STRING p89
WITH-OPEN-STREAMS p89 ZERO? p39
-142-